pax_global_header00006660000000000000000000000064146401076360014521gustar00rootroot0000000000000052 comment=f85e12c3519647c18bb49d795201a897fea6ab66 pacparser-1.4.5/000077500000000000000000000000001464010763600135105ustar00rootroot00000000000000pacparser-1.4.5/.github/000077500000000000000000000000001464010763600150505ustar00rootroot00000000000000pacparser-1.4.5/.github/dependabot.yml000066400000000000000000000002731464010763600177020ustar00rootroot00000000000000version: 2 updates: - package-ecosystem: "github-actions" directory: "/" schedule: interval: "monthly" groups: github-actions: patterns: - "*" pacparser-1.4.5/.github/workflows/000077500000000000000000000000001464010763600171055ustar00rootroot00000000000000pacparser-1.4.5/.github/workflows/build.yml000066400000000000000000000205161464010763600207330ustar00rootroot00000000000000name: Build Pacparser on: push: branches: [master] paths: - "src/**" - ".github/**" - Makefile - Dockerfile pull_request: release: types: [published] workflow_dispatch: inputs: tag: description: "Tag to run workflow for" required: true permissions: contents: read packages: write jobs: build: strategy: matrix: os: [ubuntu-latest, windows-latest, macos-13] runs-on: ${{ matrix.os }} steps: - uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1 if: ${{ !contains(github.event_name, 'workflow_dispatch') }} with: fetch-depth: 0 - name: Check out code for workflow_dispatch if: ${{ contains(github.event_name, 'workflow_dispatch') }} uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1 with: fetch-depth: 0 ref: ${{ github.event.inputs.tag }} - name: make non-windows if: ${{ matrix.os != 'windows-latest' }} run: make -C src - name: make windows if: ${{ matrix.os == 'windows-latest' }} run: make -C src -f Makefile.win32 - name: Get ref_name id: get_ref_name if: ${{ matrix.os != 'windows-latest' }} run: | if [ "${{ github.event_name }}" == "pull_request" ]; then echo "ref_name=${{ github.base_ref }}" echo "ref_name=${{ github.base_ref }}" >> $GITHUB_OUTPUT || exit 1 else echo "ref_name=${{ github.ref_name }}" echo "ref_name=${{ github.ref_name }}" >> $GITHUB_OUTPUT || exit 1 fi - name: make non-windows dist if: ${{ matrix.os != 'windows-latest' }} run: | DIST_OS_SUFFIX=${{ matrix.os }} make -C src dist ls -R src/*.zip - name: make windows dist if: ${{ matrix.os == 'windows-latest' }} run: | make -C src -f Makefile.win32 dist - name: Upload dist (non-windows) if: ${{ matrix.os != 'windows-latest' }} uses: actions/upload-artifact@65462800fd760344b1a7b4382951275a0abb4808 # v4.3.3 with: name: pacparser-dist-${{ matrix.os }} path: src/pacparser*.zip - name: Upload dist (windows) if: ${{ matrix.os == 'windows-latest' }} uses: actions/upload-artifact@65462800fd760344b1a7b4382951275a0abb4808 # v4.3.3 with: name: pacparser-dist-${{ matrix.os }} path: src/dist python-module-build: strategy: matrix: os: [ubuntu-latest, windows-latest, macos-13] python-version: ["3.8", "3.9", "3.10", "3.11", "3.12"] architecture: [x64] include: - os: macos-latest python-version: "3.11" architecture: arm64 - os: macos-latest python-version: "3.12" architecture: arm64 runs-on: ${{ matrix.os }} steps: - uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1 if: ${{ !contains(github.event_name, 'workflow_dispatch') }} with: fetch-depth: 0 - name: Check out code for workflow_dispatch if: ${{ contains(github.event_name, 'workflow_dispatch') }} uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1 with: fetch-depth: 0 ref: ${{ github.event.inputs.tag }} - name: Set up Python uses: actions/setup-python@82c7e631bb3cdc910f68e0081d67478d79c6982d # v5.1.0 with: python-version: ${{ matrix.python-version }} architecture: ${{ matrix.architecture }} - name: Set up setuptools run: | python --version python -mpip install setuptools - name: make non-windows if: ${{ matrix.os != 'windows-latest' }} run: make -C src pymod-dist - name: make windows if: ${{ matrix.os == 'windows-latest' }} run: make -C src -f Makefile.win32 pymod-dist - name: Upload dist uses: actions/upload-artifact@65462800fd760344b1a7b4382951275a0abb4808 # v4.3.3 with: name: pacparser-python-${{ matrix.python-version }}-${{ matrix.os }}-dist path: src/pymod/pacparser-python* - name: Build wheel non-linux if: ${{ matrix.os != 'ubuntu-latest' }} run: | python -m pip install wheel cd src/pymod && python setup.py bdist_wheel - uses: dorny/paths-filter@de90cc6fb38fc0963ad72b210f1f284cd68cea36 # v3.0.2 id: src_changes with: filters: | changed: - 'src/**' - name: Publish package to PyPI (non-linux) if: | (matrix.os != 'ubuntu-latest') && (steps.src_changes.outputs.changed == 'true' || startsWith(github.event.inputs.tag, 'v') || startsWith(github.ref, 'refs/tags/v')) && (github.event_name != 'pull_request') env: TWINE_USERNAME: __token__ TWINE_PASSWORD: ${{ secrets.PYPI }} run: | python -m pip install twine ls -R . twine upload src/pymod/dist/* build-linux-wheels: runs-on: ubuntu-latest steps: - uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1 if: ${{ !contains(github.event_name, 'workflow_dispatch') }} with: fetch-depth: 0 - name: Check out code for workflow_dispatch if: ${{ contains(github.event_name, 'workflow_dispatch') }} uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1 with: fetch-depth: 0 ref: ${{ github.event.inputs.tag }} - name: Set env run: | echo "PACPARSER_VERSION=$(git describe --always --tags \ --candidate=100)" >> $GITHUB_ENV - name: Set up Python uses: actions/setup-python@82c7e631bb3cdc910f68e0081d67478d79c6982d # v5.1.0 - name: Set up setuptools run: | python --version python -mpip install setuptools - name: make run: make -C src pymod - name: Build sdist run: cd src/pymod && python setup.py sdist - name: Install cibuildwheel and twine run: python -m pip install cibuildwheel twine - name: Build wheel using cibuildwheel run: | cp src/spidermonkey/libjs.a src/pacparser.o src/pacparser.h src/pymod cd src/pymod && python -m cibuildwheel --output-dir dist env: CIBW_BUILD: "cp{37,38,39,310,311,312}-manylinux*64" CIBW_ENVIRONMENT: "PACPARSER_VERSION=${{ env.PACPARSER_VERSION }}" - uses: dorny/paths-filter@de90cc6fb38fc0963ad72b210f1f284cd68cea36 # v3.0.2 id: src_changes with: filters: | changed: - 'src/**' - name: Publish package to PyPI if: | startsWith(github.event.inputs.tag, 'v') || startsWith(github.ref,'refs/tags/v') ||steps.src_changes.outputs.changed == 'true' env: TWINE_USERNAME: __token__ TWINE_PASSWORD: ${{ secrets.PYPI }} run: | twine upload src/pymod/dist/* build_and_push_docker_multiarch: name: Build and push multiarch docker image if: | github.repository == 'manugarg/pacparser' && (github.ref == 'refs/heads/master' || startswith(github.ref, 'refs/heads/docker') || startsWith(github.ref, 'refs/tags/v')) runs-on: ubuntu-latest steps: - name: Check out code into the Go module directory if: ${{ !contains(github.event_name, 'workflow_dispatch') }} uses: actions/checkout@v4 with: fetch-depth: 0 - name: Check out code into the Go module directory if: ${{ contains(github.event_name, 'workflow_dispatch') }} uses: actions/checkout@v4 with: fetch-depth: 0 ref: ${{ github.event.inputs.tag }} - name: Set up QEMU uses: docker/setup-qemu-action@v3 - name: Set up Docker Buildx uses: docker/setup-buildx-action@v3 - name: Login to GHCR uses: docker/login-action@v3 with: registry: ghcr.io username: ${{ github.repository_owner }} password: ${{ secrets.GITHUB_TOKEN }} - name: Build and push release Docker Image (main-ghcr) run: make docker_multiarch DOCKER_IMAGE=ghcr.io/manugarg/pactester pacparser-1.4.5/.github/workflows/sonar.yml000066400000000000000000000017331464010763600207560ustar00rootroot00000000000000name: SonarCloud on: push: branches: - master pull_request: types: [opened, synchronize, reopened] permissions: read-all jobs: build: name: Analyze if: github.repository == 'manugarg/pacparser' && (github.event_name == 'push' || github.event.pull_request.head.repo.full_name == github.repository) runs-on: ubuntu-latest steps: - uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1 with: fetch-depth: 0 # Shallow clones should be disabled for a better relevancy of analysis - name: Install sonar-scanner and build-wrapper uses: SonarSource/sonarcloud-github-c-cpp@e4882e1621ad2fb48dddfa48287411bed34789b1 # v2.0.2 - name: Run sonar-scanner env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }} run: | sudo apt install -y bear make -C src clean bear -- make -C src sonar-scanner pacparser-1.4.5/.gitignore000066400000000000000000000005671464010763600155100ustar00rootroot00000000000000# Object files *.o # Libraries *.lib *.a # Shared objects (inc. Windows DLLs) *.dll *.so *.so.* *.dylib *.dSYM # Executables *.exe *.out *.app # Packages tools/packages *.dmg *.pkg # Other build files *buildstamp src/spidermonkey/js src/pactester # OS specific files .DS_Store # build **build # Sonar scanner .scannerwork # .json config *.json pactester info.plist pacparser-1.4.5/COPYING000066400000000000000000000167271464010763600145600ustar00rootroot00000000000000 GNU LESSER GENERAL PUBLIC LICENSE Version 3, 29 June 2007 Copyright (C) 2007 Free Software Foundation, Inc. Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. This version of the GNU Lesser General Public License incorporates the terms and conditions of version 3 of the GNU General Public License, supplemented by the additional permissions listed below. 0. Additional Definitions. As used herein, "this License" refers to version 3 of the GNU Lesser General Public License, and the "GNU GPL" refers to version 3 of the GNU General Public License. "The Library" refers to a covered work governed by this License, other than an Application or a Combined Work as defined below. An "Application" is any work that makes use of an interface provided by the Library, but which is not otherwise based on the Library. Defining a subclass of a class defined by the Library is deemed a mode of using an interface provided by the Library. A "Combined Work" is a work produced by combining or linking an Application with the Library. The particular version of the Library with which the Combined Work was made is also called the "Linked Version". The "Minimal Corresponding Source" for a Combined Work means the Corresponding Source for the Combined Work, excluding any source code for portions of the Combined Work that, considered in isolation, are based on the Application, and not on the Linked Version. The "Corresponding Application Code" for a Combined Work means the object code and/or source code for the Application, including any data and utility programs needed for reproducing the Combined Work from the Application, but excluding the System Libraries of the Combined Work. 1. Exception to Section 3 of the GNU GPL. You may convey a covered work under sections 3 and 4 of this License without being bound by section 3 of the GNU GPL. 2. Conveying Modified Versions. If you modify a copy of the Library, and, in your modifications, a facility refers to a function or data to be supplied by an Application that uses the facility (other than as an argument passed when the facility is invoked), then you may convey a copy of the modified version: a) under this License, provided that you make a good faith effort to ensure that, in the event an Application does not supply the function or data, the facility still operates, and performs whatever part of its purpose remains meaningful, or b) under the GNU GPL, with none of the additional permissions of this License applicable to that copy. 3. Object Code Incorporating Material from Library Header Files. The object code form of an Application may incorporate material from a header file that is part of the Library. You may convey such object code under terms of your choice, provided that, if the incorporated material is not limited to numerical parameters, data structure layouts and accessors, or small macros, inline functions and templates (ten or fewer lines in length), you do both of the following: a) Give prominent notice with each copy of the object code that the Library is used in it and that the Library and its use are covered by this License. b) Accompany the object code with a copy of the GNU GPL and this license document. 4. Combined Works. You may convey a Combined Work under terms of your choice that, taken together, effectively do not restrict modification of the portions of the Library contained in the Combined Work and reverse engineering for debugging such modifications, if you also do each of the following: a) Give prominent notice with each copy of the Combined Work that the Library is used in it and that the Library and its use are covered by this License. b) Accompany the Combined Work with a copy of the GNU GPL and this license document. c) For a Combined Work that displays copyright notices during execution, include the copyright notice for the Library among these notices, as well as a reference directing the user to the copies of the GNU GPL and this license document. d) Do one of the following: 0) Convey the Minimal Corresponding Source under the terms of this License, and the Corresponding Application Code in a form suitable for, and under terms that permit, the user to recombine or relink the Application with a modified version of the Linked Version to produce a modified Combined Work, in the manner specified by section 6 of the GNU GPL for conveying Corresponding Source. 1) Use a suitable shared library mechanism for linking with the Library. A suitable mechanism is one that (a) uses at run time a copy of the Library already present on the user's computer system, and (b) will operate properly with a modified version of the Library that is interface-compatible with the Linked Version. e) Provide Installation Information, but only if you would otherwise be required to provide such information under section 6 of the GNU GPL, and only to the extent that such information is necessary to install and execute a modified version of the Combined Work produced by recombining or relinking the Application with a modified version of the Linked Version. (If you use option 4d0, the Installation Information must accompany the Minimal Corresponding Source and Corresponding Application Code. If you use option 4d1, you must provide the Installation Information in the manner specified by section 6 of the GNU GPL for conveying Corresponding Source.) 5. Combined Libraries. You may place library facilities that are a work based on the Library side by side in a single library together with other library facilities that are not Applications and are not covered by this License, and convey such a combined library under terms of your choice, if you do both of the following: a) Accompany the combined library with a copy of the same work based on the Library, uncombined with any other library facilities, conveyed under the terms of this License. b) Give prominent notice with the combined library that part of it is a work based on the Library, and explaining where to find the accompanying uncombined form of the same work. 6. Revised Versions of the GNU Lesser General Public License. The Free Software Foundation may publish revised and/or new versions of the GNU Lesser General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns. Each version is given a distinguishing version number. If the Library as you received it specifies that a certain numbered version of the GNU Lesser General Public License "or any later version" applies to it, you have the option of following the terms and conditions either of that published version or of any later version published by the Free Software Foundation. If the Library as you received it does not specify a version number of the GNU Lesser General Public License, you may choose any version of the GNU Lesser General Public License ever published by the Free Software Foundation. If the Library as you received it specifies that a proxy can decide whether future versions of the GNU Lesser General Public License shall apply, that proxy's public statement of acceptance of any version is permanent authorization for you to choose that version for the Library. pacparser-1.4.5/Dockerfile000066400000000000000000000013261464010763600155040ustar00rootroot00000000000000# Use Alpine Linux as the base image FROM alpine:latest as build-stage # Install build dependencies RUN apk update && apk add --no-cache \ gcc \ git \ g++ \ bash \ make WORKDIR /build COPY src /build RUN make pactester # Final stage FROM alpine:latest WORKDIR /app COPY --from=build-stage /build/pactester /app/ # Metadata params ARG BUILD_DATE ARG VERSION ARG VCS_REF # Metadata LABEL org.label-schema.build-date=$BUILD_DATE \ org.label-schema.name="Pactester" \ org.label-schema.vcs-url="https://github.com/manugarg/pacparser" \ org.label-schema.vcs-ref=$VCS_REF \ org.label-schema.version=$VERSION \ com.microscaling.license="Apache-2.0" ENTRYPOINT ["/app/pactester"]pacparser-1.4.5/INSTALL000066400000000000000000000031261464010763600145430ustar00rootroot00000000000000For Python module, it's recommended to use "pip" for the installation: pip install pacparser pip install pacparser==1.3.8.dev15 (specific version) For other pre-built binaries, download them from: https://github.com/manugarg/pacparser/releases You can also download the latest binaries from the Github actions artifacts: https://github.com/manugarg/pacparser/actions Compile From Source: ################### On Unix-like Systems: ==================== Get the latest source code from github (git) repository by following instructions at: https://github.com/manugarg/pacparser * pacparser C library and pactester: ---------------------------------- Compiling and installing pacparser is as easy as running the following commands: => make -C src => sudo make -C src install * pacparser python module: ------------------------------------------------ To compile and install pacparser python module: => make -C src pymod => sudo make -C src install-pymod If it failed while running runtests.py like this; ``` File "../tests/runtests.py", line 79, in runtests raise Exception('Tests failed. Got "%s", expected "%s"' % (result, expected_result)) Exception: Tests failed. Got "END-OF-SCRIPT", expected "isResolvable" ``` Please set an environment variable NO_INTERNET. Then it won't require internet and pass the test. ``` $ export NO_INTERNET=1 && make -C src install-pymod ``` On Win-32 Systems: ================= Compiling pacparser for Windows is a rather involved process and is documented in detail in README.win32 file included with this package. pacparser-1.4.5/Makefile000066400000000000000000000014731464010763600151550ustar00rootroot00000000000000# Copyright (C) 2024 Manu Garg. # Author: Manu Garg # # Makefile for pacparser docker image. -include version.mk VERSION ?= $(shell git describe --always --tags --candidate=100) GIT_TAG := $(shell git describe --exact-match --exclude tip --tags HEAD 2>/dev/null || /bin/true) GIT_COMMIT = $(strip $(shell git rev-parse --short HEAD)) DOCKER_IMAGE ?= manugarg/pactester ifeq "$(GIT_TAG)" "" DOCKER_TAGS := -t $(DOCKER_IMAGE):master -t $(DOCKER_IMAGE):main else DOCKER_TAGS := -t $(DOCKER_IMAGE):$(GIT_TAG) -t $(DOCKER_IMAGE):latest endif docker_multiarch: Dockerfile docker buildx build --push \ --build-arg BUILD_DATE=`date -u +"%Y-%m-%dT%H:%M:%SZ"` \ --build-arg VERSION=$(VERSION) \ --build-arg VCS_REF=$(GIT_COMMIT) \ --platform linux/amd64,linux/arm64,linux/arm/v7 \ $(DOCKER_TAGS) . pacparser-1.4.5/README.md000066400000000000000000000102231464010763600147650ustar00rootroot00000000000000[![Build Status](https://github.com/manugarg/pacparser/actions/workflows/build.yml/badge.svg)](https://github.com/manugarg/pacparser/actions/workflows/build.yml) [![PyPI version](https://badge.fury.io/py/pacparser.svg)](https://badge.fury.io/py/pacparser) [![PyPI Downloads](https://static.pepy.tech/badge/pacparser)](https://pepy.tech/project/pacparser) [![Packaging status](https://repology.org/badge/tiny-repos/pacparser.svg)](https://repology.org/project/pacparser/versions) # [Pacparser](http://pacparser.manugarg.com) ***[pacparser.manugarg.com](http://pacparser.manugarg.com)*** Pacparser is a library to parse proxy auto-config (PAC) files. Proxy auto-config files are a vastly used proxy configuration method these days. Web browsers can use a PAC file to determine which proxy server to use or whether to go direct for a given URL. PAC files are written in JavaScript and can be programmed to return different proxy methods (e.g., `"PROXY proxy1:port; DIRECT"`) depending upon URL, source IP address, protocol, time of the day etc. PAC files introduce a lot of possibilities. Please look at the wikipedia entry for Proxy auto-config () for more information. The idea behind pacparser is to make it easy to add PAC-file parsing capability to any program (C and python supported right now). It comes as a shared C library and a python module which can be used to make any C or python program PAC scripts aware. ### Implementation Pacparser makes use of the Mozilla's JavaScript interpreter SpiderMonkey to parse PAC files (which are nothing but javascripts). Apart from that, proxy auto-config standard assumes availability of some functions which are not part of the standard JavaScript. Pacparser uses Mozilla's PAC implementation to define all these functions except for a couple of dns functions which are defined by pacparser itself. As a result, pacparser is as close to standard as it gets :) ### Install For Python module, you can use pip. Pre-built module is available for `64-bit Linux, Windows, MacOS-Intel, and MacOS-ARM`, for Python `3.7, 3.8, 3.9, 3.10 and 3.11`. ``` python -m pip install pacparser python -m pip install pacparser==1.3.8.dev15 (specific version) ``` For other pre-built binaries, download them from the project's [releases]( https://github.com/manugarg/pacparser/releases) page. You can also download the latest binaries from the [Github actions]( https://github.com/manugarg/pacparser/actions) artifcacts. See [INSTALL](https://github.com/manugarg/pacparser/blob/master/INSTALL) for how to compile pacparser from the source. ### How to use it? Pacparser comes as a shared library (`libpacparser.so` on Linux, `libpacparser.dylib` on MacOS, and pacparser.dll on windows) as well as a python module. Using it is as easy compiling your C programs against it or importing pacparser module in your python programs. ### Usage Examples #### Python: ```python >>> import pacparser >>> pacparser.init() >>> pacparser.parse_pac('examples/wpad.dat') >>> pacparser.find_proxy('http://www.google.com', 'www.google.com') 'DIRECT' >>> pacparser.setmyip("192.168.1.134") >>> pacparser.find_proxy('http://www.google.com', 'www.google.com') 'PROXY proxy1.manugarg.com:3128; PROXY proxy2.manugarg.com:3128; DIRECT' >>> pacparser.find_proxy('http://www2.manugarg.com', 'www2.manugarg.com') 'DIRECT' >>> pacparser.cleanup() >>> ``` #### C ```C #include int pacparser_init(); int pacparser_parse_pac(char* pacfile); char *pacparser_find_proxy(char *url, char *host); void pacparser_cleanup(); int main(int argc, char* argv[]) { char *proxy; pacparser_init(); pacparser_parse_pac(argv[1]); proxy = pacparser_find_proxy(argv[2], argv[3]); printf("%s\n", proxy); pacparser_cleanup(); } ``` ``` manugarg@hobbiton:~$ gcc -o pactest pactest.c -lpacparser manugarg@hobbiton:~$ ./pactest wpad.dat http://www.google.com www.google.com PROXY proxy1.manugarg.com:3128; PROXY proxy2.manugarg.com:3128; DIRECT ``` #### Platforms pacparser has been tested to work on Linux (all architectures supported by Debian), FreeBSD, Mac OS X and Win32 systems. #### Homepage http://pacparser.manugarg.com Author: [Manu Garg](http://github.com/manugarg) pacparser-1.4.5/README.win32.md000066400000000000000000000043401464010763600157310ustar00rootroot00000000000000This file is about the instructions to build (and use) pacparser on Windows. For general information on pacparser, please have a look at README in the same directory. Building pacparser on Windows: ----------------------------- * Install MinGW64 tools through [msys2]( https://github.com/msys2/msys2-installer/releases). * Rename mingw32-make.exe to make.exe. ``` rename C:\msys64\mingw64\bin\mingw32-make.exe C:\msys64\mingw64\bin\make.exe ``` * Add your MinGW directory's bin (`C:\msys64\mingw64\bin`) to your system path variable. * Install latest [python](http://www.python.org/). You'll need it to build the python module. * Git-clone the pacparser source code, or download the source code tarball from the [releases]( https://github.com/manugarg/pacparser/releases) page, and extract it somewhere, say `C:\workspace`. * Compile pacparser and create ditribution. ``` cd C:\workspace\pacparser-* make -C src -f Makefile.win32 dist ``` * Compile pacparser python module and copy required files to a directory (dist). ``` make -C src -f Makefile.win32 pymod-3.9 ``` * Compile pacparser python module and copy required files to a directory (dist). ``` make -C src -f Makefile.win32 pymod-dist-3.9 ``` ## Using pacparser on Windows: ### In C programs: Make sure that you have pacparser.dll in the sytem path somewhere (current directory would do just fine for testing purpose). ``` Change to your program's directory: => cd c:\workspace\pacparser-1.3.8\examples Copy pacparser.dll here and compile your program: => gcc -o pactest pactest.c -lpacparser -L. Run your program: => pactest wpad.dat http://www.google.com www.google.com 'PROXY proxy1.manugarg.com:3128; PROXY proxy2.manugarg.com:3128; DIRECT' ``` ### In python programs: Install pacparser python module by running install.py in the distribution folder. You'll then be able to use pacparser python module in the following manner: ```python => python >>> import pacparser >>> pacparser.init() >>> pacparser.parse_pac('examples/wpad.dat') >>> pacparser.find_proxy('http://www.google.com', 'www.google.com') 'PROXY proxy1.manugarg.com:3128; PROXY proxy2.manugarg.com:3128; DIRECT' >>> pacparser.cleanup() ``` pacparser-1.4.5/SECURITY.md000066400000000000000000000011041464010763600152750ustar00rootroot00000000000000# Security Policy If you have discovered a security vulnerability in this project, please report it privately. **Do not disclose it as a public issue.** This gives us time to work with you to fix the issue before public exposure, reducing the chance that the exploit will be used before a patch is released. Please disclose it at [security advisory](https://github.com/manugarg/pacparser/security/advisories/new). This project is maintained by a team of volunteers on a reasonable-effort basis. As such, please give us at least 90 days to work on a fix before public exposure. pacparser-1.4.5/docs/000077500000000000000000000000001464010763600144405ustar00rootroot00000000000000pacparser-1.4.5/docs/doxygen.config000066400000000000000000002364321464010763600173160ustar00rootroot00000000000000# Doxyfile 1.8.3.1 # This file describes the settings to be used by the documentation system # doxygen (www.doxygen.org) for a project # # All text after a hash (#) is considered a comment and will be ignored # The format is: # TAG = value [value, ...] # For lists items can also be appended using: # TAG += value [value, ...] # Values that contain spaces should be placed between quotes (" ") #--------------------------------------------------------------------------- # Project related configuration options #--------------------------------------------------------------------------- # This tag specifies the encoding used for all characters in the config file # that follow. The default is UTF-8 which is also the encoding used for all # text before the first occurrence of this tag. Doxygen uses libiconv (or the # iconv built into libc) for the transcoding. See # http://www.gnu.org/software/libiconv for the list of possible encodings. DOXYFILE_ENCODING = UTF-8 # The PROJECT_NAME tag is a single word (or sequence of words) that should # identify the project. Note that if you do not use Doxywizard you need # to put quotes around the project name if it contains spaces. PROJECT_NAME = Pacparser # The PROJECT_NUMBER tag can be used to enter a project or revision number. # This could be handy for archiving the generated documentation or # if some version control system is used. PROJECT_NUMBER = # Using the PROJECT_BRIEF tag one can provide an optional one line description # for a project that appears at the top of each page and should give viewer # a quick idea about the purpose of the project. Keep the description short. PROJECT_BRIEF = "A library to make your web software pac (proxy auto-config) files intelligent." # With the PROJECT_LOGO tag one can specify an logo or icon that is # included in the documentation. The maximum height of the logo should not # exceed 55 pixels and the maximum width should not exceed 200 pixels. # Doxygen will copy the logo to the output directory. PROJECT_LOGO = # The OUTPUT_DIRECTORY tag is used to specify the (relative or absolute) # base path where the generated documentation will be put. # If a relative path is entered, it will be relative to the location # where doxygen was started. If left blank the current directory will be used. OUTPUT_DIRECTORY = # If the CREATE_SUBDIRS tag is set to YES, then doxygen will create # 4096 sub-directories (in 2 levels) under the output directory of each output # format and will distribute the generated files over these directories. # Enabling this option can be useful when feeding doxygen a huge amount of # source files, where putting all generated files in the same directory would # otherwise cause performance problems for the file system. CREATE_SUBDIRS = NO # The OUTPUT_LANGUAGE tag is used to specify the language in which all # documentation generated by doxygen is written. Doxygen will use this # information to generate all constant output in the proper language. # The default language is English, other supported languages are: # Afrikaans, Arabic, Brazilian, Catalan, Chinese, Chinese-Traditional, # Croatian, Czech, Danish, Dutch, Esperanto, Farsi, Finnish, French, German, # Greek, Hungarian, Italian, Japanese, Japanese-en (Japanese with English # messages), Korean, Korean-en, Lithuanian, Norwegian, Macedonian, Persian, # Polish, Portuguese, Romanian, Russian, Serbian, Serbian-Cyrillic, Slovak, # Slovene, Spanish, Swedish, Ukrainian, and Vietnamese. OUTPUT_LANGUAGE = English # If the BRIEF_MEMBER_DESC tag is set to YES (the default) Doxygen will # include brief member descriptions after the members that are listed in # the file and class documentation (similar to JavaDoc). # Set to NO to disable this. BRIEF_MEMBER_DESC = YES # If the REPEAT_BRIEF tag is set to YES (the default) Doxygen will prepend # the brief description of a member or function before the detailed description. # Note: if both HIDE_UNDOC_MEMBERS and BRIEF_MEMBER_DESC are set to NO, the # brief descriptions will be completely suppressed. REPEAT_BRIEF = YES # This tag implements a quasi-intelligent brief description abbreviator # that is used to form the text in various listings. Each string # in this list, if found as the leading text of the brief description, will be # stripped from the text and the result after processing the whole list, is # used as the annotated text. Otherwise, the brief description is used as-is. # If left blank, the following values are used ("$name" is automatically # replaced with the name of the entity): "The $name class" "The $name widget" # "The $name file" "is" "provides" "specifies" "contains" # "represents" "a" "an" "the" ABBREVIATE_BRIEF = "The $name class" \ "The $name widget" \ "The $name file" \ is \ provides \ specifies \ contains \ represents \ a \ an \ the # If the ALWAYS_DETAILED_SEC and REPEAT_BRIEF tags are both set to YES then # Doxygen will generate a detailed section even if there is only a brief # description. ALWAYS_DETAILED_SEC = NO # If the INLINE_INHERITED_MEMB tag is set to YES, doxygen will show all # inherited members of a class in the documentation of that class as if those # members were ordinary class members. Constructors, destructors and assignment # operators of the base classes will not be shown. INLINE_INHERITED_MEMB = NO # If the FULL_PATH_NAMES tag is set to YES then Doxygen will prepend the full # path before files name in the file list and in the header files. If set # to NO the shortest path that makes the file name unique will be used. FULL_PATH_NAMES = NO # If the FULL_PATH_NAMES tag is set to YES then the STRIP_FROM_PATH tag # can be used to strip a user-defined part of the path. Stripping is # only done if one of the specified strings matches the left-hand part of # the path. The tag can be used to show relative paths in the file list. # If left blank the directory from which doxygen is run is used as the # path to strip. Note that you specify absolute paths here, but also # relative paths, which will be relative from the directory where doxygen is # started. STRIP_FROM_PATH = # The STRIP_FROM_INC_PATH tag can be used to strip a user-defined part of # the path mentioned in the documentation of a class, which tells # the reader which header file to include in order to use a class. # If left blank only the name of the header file containing the class # definition is used. Otherwise one should specify the include paths that # are normally passed to the compiler using the -I flag. STRIP_FROM_INC_PATH = # If the SHORT_NAMES tag is set to YES, doxygen will generate much shorter # (but less readable) file names. This can be useful if your file system # doesn't support long names like on DOS, Mac, or CD-ROM. SHORT_NAMES = NO # If the JAVADOC_AUTOBRIEF tag is set to YES then Doxygen # will interpret the first line (until the first dot) of a JavaDoc-style # comment as the brief description. If set to NO, the JavaDoc # comments will behave just like regular Qt-style comments # (thus requiring an explicit @brief command for a brief description.) JAVADOC_AUTOBRIEF = NO # If the QT_AUTOBRIEF tag is set to YES then Doxygen will # interpret the first line (until the first dot) of a Qt-style # comment as the brief description. If set to NO, the comments # will behave just like regular Qt-style comments (thus requiring # an explicit \brief command for a brief description.) QT_AUTOBRIEF = NO # The MULTILINE_CPP_IS_BRIEF tag can be set to YES to make Doxygen # treat a multi-line C++ special comment block (i.e. a block of //! or /// # comments) as a brief description. This used to be the default behaviour. # The new default is to treat a multi-line C++ comment block as a detailed # description. Set this tag to YES if you prefer the old behaviour instead. MULTILINE_CPP_IS_BRIEF = NO # If the INHERIT_DOCS tag is set to YES (the default) then an undocumented # member inherits the documentation from any documented member that it # re-implements. INHERIT_DOCS = YES # If the SEPARATE_MEMBER_PAGES tag is set to YES, then doxygen will produce # a new page for each member. If set to NO, the documentation of a member will # be part of the file/class/namespace that contains it. SEPARATE_MEMBER_PAGES = NO # The TAB_SIZE tag can be used to set the number of spaces in a tab. # Doxygen uses this value to replace tabs by spaces in code fragments. TAB_SIZE = 4 # This tag can be used to specify a number of aliases that acts # as commands in the documentation. An alias has the form "name=value". # For example adding "sideeffect=\par Side Effects:\n" will allow you to # put the command \sideeffect (or @sideeffect) in the documentation, which # will result in a user-defined paragraph with heading "Side Effects:". # You can put \n's in the value part of an alias to insert newlines. ALIASES = # This tag can be used to specify a number of word-keyword mappings (TCL only). # A mapping has the form "name=value". For example adding # "class=itcl::class" will allow you to use the command class in the # itcl::class meaning. TCL_SUBST = # Set the OPTIMIZE_OUTPUT_FOR_C tag to YES if your project consists of C # sources only. Doxygen will then generate output that is more tailored for C. # For instance, some of the names that are used will be different. The list # of all members will be omitted, etc. OPTIMIZE_OUTPUT_FOR_C = YES # Set the OPTIMIZE_OUTPUT_JAVA tag to YES if your project consists of Java # sources only. Doxygen will then generate output that is more tailored for # Java. For instance, namespaces will be presented as packages, qualified # scopes will look different, etc. OPTIMIZE_OUTPUT_JAVA = NO # Set the OPTIMIZE_FOR_FORTRAN tag to YES if your project consists of Fortran # sources only. Doxygen will then generate output that is more tailored for # Fortran. OPTIMIZE_FOR_FORTRAN = NO # Set the OPTIMIZE_OUTPUT_VHDL tag to YES if your project consists of VHDL # sources. Doxygen will then generate output that is tailored for # VHDL. OPTIMIZE_OUTPUT_VHDL = NO # Doxygen selects the parser to use depending on the extension of the files it # parses. With this tag you can assign which parser to use for a given # extension. Doxygen has a built-in mapping, but you can override or extend it # using this tag. The format is ext=language, where ext is a file extension, # and language is one of the parsers supported by doxygen: IDL, Java, # Javascript, CSharp, C, C++, D, PHP, Objective-C, Python, Fortran, VHDL, C, # C++. For instance to make doxygen treat .inc files as Fortran files (default # is PHP), and .f files as C (default is Fortran), use: inc=Fortran f=C. Note # that for custom extensions you also need to set FILE_PATTERNS otherwise the # files are not read by doxygen. EXTENSION_MAPPING = # If MARKDOWN_SUPPORT is enabled (the default) then doxygen pre-processes all # comments according to the Markdown format, which allows for more readable # documentation. See http://daringfireball.net/projects/markdown/ for details. # The output of markdown processing is further processed by doxygen, so you # can mix doxygen, HTML, and XML commands with Markdown formatting. # Disable only in case of backward compatibilities issues. MARKDOWN_SUPPORT = YES # When enabled doxygen tries to link words that correspond to documented classes, # or namespaces to their corresponding documentation. Such a link can be # prevented in individual cases by by putting a % sign in front of the word or # globally by setting AUTOLINK_SUPPORT to NO. AUTOLINK_SUPPORT = YES # If you use STL classes (i.e. std::string, std::vector, etc.) but do not want # to include (a tag file for) the STL sources as input, then you should # set this tag to YES in order to let doxygen match functions declarations and # definitions whose arguments contain STL classes (e.g. func(std::string); v.s. # func(std::string) {}). This also makes the inheritance and collaboration # diagrams that involve STL classes more complete and accurate. BUILTIN_STL_SUPPORT = NO # If you use Microsoft's C++/CLI language, you should set this option to YES to # enable parsing support. CPP_CLI_SUPPORT = NO # Set the SIP_SUPPORT tag to YES if your project consists of sip sources only. # Doxygen will parse them like normal C++ but will assume all classes use public # instead of private inheritance when no explicit protection keyword is present. SIP_SUPPORT = NO # For Microsoft's IDL there are propget and propput attributes to indicate # getter and setter methods for a property. Setting this option to YES (the # default) will make doxygen replace the get and set methods by a property in # the documentation. This will only work if the methods are indeed getting or # setting a simple type. If this is not the case, or you want to show the # methods anyway, you should set this option to NO. IDL_PROPERTY_SUPPORT = YES # If member grouping is used in the documentation and the DISTRIBUTE_GROUP_DOC # tag is set to YES, then doxygen will reuse the documentation of the first # member in the group (if any) for the other members of the group. By default # all members of a group must be documented explicitly. DISTRIBUTE_GROUP_DOC = NO # Set the SUBGROUPING tag to YES (the default) to allow class member groups of # the same type (for instance a group of public functions) to be put as a # subgroup of that type (e.g. under the Public Functions section). Set it to # NO to prevent subgrouping. Alternatively, this can be done per class using # the \nosubgrouping command. SUBGROUPING = YES # When the INLINE_GROUPED_CLASSES tag is set to YES, classes, structs and # unions are shown inside the group in which they are included (e.g. using # @ingroup) instead of on a separate page (for HTML and Man pages) or # section (for LaTeX and RTF). INLINE_GROUPED_CLASSES = NO # When the INLINE_SIMPLE_STRUCTS tag is set to YES, structs, classes, and # unions with only public data fields will be shown inline in the documentation # of the scope in which they are defined (i.e. file, namespace, or group # documentation), provided this scope is documented. If set to NO (the default), # structs, classes, and unions are shown on a separate page (for HTML and Man # pages) or section (for LaTeX and RTF). INLINE_SIMPLE_STRUCTS = NO # When TYPEDEF_HIDES_STRUCT is enabled, a typedef of a struct, union, or enum # is documented as struct, union, or enum with the name of the typedef. So # typedef struct TypeS {} TypeT, will appear in the documentation as a struct # with name TypeT. When disabled the typedef will appear as a member of a file, # namespace, or class. And the struct will be named TypeS. This can typically # be useful for C code in case the coding convention dictates that all compound # types are typedef'ed and only the typedef is referenced, never the tag name. TYPEDEF_HIDES_STRUCT = NO # The SYMBOL_CACHE_SIZE determines the size of the internal cache use to # determine which symbols to keep in memory and which to flush to disk. # When the cache is full, less often used symbols will be written to disk. # For small to medium size projects (<1000 input files) the default value is # probably good enough. For larger projects a too small cache size can cause # doxygen to be busy swapping symbols to and from disk most of the time # causing a significant performance penalty. # If the system has enough physical memory increasing the cache will improve the # performance by keeping more symbols in memory. Note that the value works on # a logarithmic scale so increasing the size by one will roughly double the # memory usage. The cache size is given by this formula: # 2^(16+SYMBOL_CACHE_SIZE). The valid range is 0..9, the default is 0, # corresponding to a cache size of 2^16 = 65536 symbols. SYMBOL_CACHE_SIZE = 0 # Similar to the SYMBOL_CACHE_SIZE the size of the symbol lookup cache can be # set using LOOKUP_CACHE_SIZE. This cache is used to resolve symbols given # their name and scope. Since this can be an expensive process and often the # same symbol appear multiple times in the code, doxygen keeps a cache of # pre-resolved symbols. If the cache is too small doxygen will become slower. # If the cache is too large, memory is wasted. The cache size is given by this # formula: 2^(16+LOOKUP_CACHE_SIZE). The valid range is 0..9, the default is 0, # corresponding to a cache size of 2^16 = 65536 symbols. LOOKUP_CACHE_SIZE = 0 #--------------------------------------------------------------------------- # Build related configuration options #--------------------------------------------------------------------------- # If the EXTRACT_ALL tag is set to YES doxygen will assume all entities in # documentation are documented, even if no documentation was available. # Private class members and static file members will be hidden unless # the EXTRACT_PRIVATE and EXTRACT_STATIC tags are set to YES EXTRACT_ALL = NO # If the EXTRACT_PRIVATE tag is set to YES all private members of a class # will be included in the documentation. EXTRACT_PRIVATE = NO # If the EXTRACT_PACKAGE tag is set to YES all members with package or internal # scope will be included in the documentation. EXTRACT_PACKAGE = NO # If the EXTRACT_STATIC tag is set to YES all static members of a file # will be included in the documentation. EXTRACT_STATIC = NO # If the EXTRACT_LOCAL_CLASSES tag is set to YES classes (and structs) # defined locally in source files will be included in the documentation. # If set to NO only classes defined in header files are included. EXTRACT_LOCAL_CLASSES = YES # This flag is only useful for Objective-C code. When set to YES local # methods, which are defined in the implementation section but not in # the interface are included in the documentation. # If set to NO (the default) only methods in the interface are included. EXTRACT_LOCAL_METHODS = NO # If this flag is set to YES, the members of anonymous namespaces will be # extracted and appear in the documentation as a namespace called # 'anonymous_namespace{file}', where file will be replaced with the base # name of the file that contains the anonymous namespace. By default # anonymous namespaces are hidden. EXTRACT_ANON_NSPACES = NO # If the HIDE_UNDOC_MEMBERS tag is set to YES, Doxygen will hide all # undocumented members of documented classes, files or namespaces. # If set to NO (the default) these members will be included in the # various overviews, but no documentation section is generated. # This option has no effect if EXTRACT_ALL is enabled. HIDE_UNDOC_MEMBERS = YES # If the HIDE_UNDOC_CLASSES tag is set to YES, Doxygen will hide all # undocumented classes that are normally visible in the class hierarchy. # If set to NO (the default) these classes will be included in the various # overviews. This option has no effect if EXTRACT_ALL is enabled. HIDE_UNDOC_CLASSES = NO # If the HIDE_FRIEND_COMPOUNDS tag is set to YES, Doxygen will hide all # friend (class|struct|union) declarations. # If set to NO (the default) these declarations will be included in the # documentation. HIDE_FRIEND_COMPOUNDS = NO # If the HIDE_IN_BODY_DOCS tag is set to YES, Doxygen will hide any # documentation blocks found inside the body of a function. # If set to NO (the default) these blocks will be appended to the # function's detailed documentation block. HIDE_IN_BODY_DOCS = YES # The INTERNAL_DOCS tag determines if documentation # that is typed after a \internal command is included. If the tag is set # to NO (the default) then the documentation will be excluded. # Set it to YES to include the internal documentation. INTERNAL_DOCS = NO # If the CASE_SENSE_NAMES tag is set to NO then Doxygen will only generate # file names in lower-case letters. If set to YES upper-case letters are also # allowed. This is useful if you have classes or files whose names only differ # in case and if your file system supports case sensitive file names. Windows # and Mac users are advised to set this option to NO. CASE_SENSE_NAMES = NO # If the HIDE_SCOPE_NAMES tag is set to NO (the default) then Doxygen # will show members with their full class and namespace scopes in the # documentation. If set to YES the scope will be hidden. HIDE_SCOPE_NAMES = YES # If the SHOW_INCLUDE_FILES tag is set to YES (the default) then Doxygen # will put a list of the files that are included by a file in the documentation # of that file. SHOW_INCLUDE_FILES = YES # If the FORCE_LOCAL_INCLUDES tag is set to YES then Doxygen # will list include files with double quotes in the documentation # rather than with sharp brackets. FORCE_LOCAL_INCLUDES = NO # If the INLINE_INFO tag is set to YES (the default) then a tag [inline] # is inserted in the documentation for inline members. INLINE_INFO = YES # If the SORT_MEMBER_DOCS tag is set to YES (the default) then doxygen # will sort the (detailed) documentation of file and class members # alphabetically by member name. If set to NO the members will appear in # declaration order. SORT_MEMBER_DOCS = NO # If the SORT_BRIEF_DOCS tag is set to YES then doxygen will sort the # brief documentation of file, namespace and class members alphabetically # by member name. If set to NO (the default) the members will appear in # declaration order. SORT_BRIEF_DOCS = NO # If the SORT_MEMBERS_CTORS_1ST tag is set to YES then doxygen # will sort the (brief and detailed) documentation of class members so that # constructors and destructors are listed first. If set to NO (the default) # the constructors will appear in the respective orders defined by # SORT_MEMBER_DOCS and SORT_BRIEF_DOCS. # This tag will be ignored for brief docs if SORT_BRIEF_DOCS is set to NO # and ignored for detailed docs if SORT_MEMBER_DOCS is set to NO. SORT_MEMBERS_CTORS_1ST = NO # If the SORT_GROUP_NAMES tag is set to YES then doxygen will sort the # hierarchy of group names into alphabetical order. If set to NO (the default) # the group names will appear in their defined order. SORT_GROUP_NAMES = NO # If the SORT_BY_SCOPE_NAME tag is set to YES, the class list will be # sorted by fully-qualified names, including namespaces. If set to # NO (the default), the class list will be sorted only by class name, # not including the namespace part. # Note: This option is not very useful if HIDE_SCOPE_NAMES is set to YES. # Note: This option applies only to the class list, not to the # alphabetical list. SORT_BY_SCOPE_NAME = NO # If the STRICT_PROTO_MATCHING option is enabled and doxygen fails to # do proper type resolution of all parameters of a function it will reject a # match between the prototype and the implementation of a member function even # if there is only one candidate or it is obvious which candidate to choose # by doing a simple string match. By disabling STRICT_PROTO_MATCHING doxygen # will still accept a match between prototype and implementation in such cases. STRICT_PROTO_MATCHING = NO # The GENERATE_TODOLIST tag can be used to enable (YES) or # disable (NO) the todo list. This list is created by putting \todo # commands in the documentation. GENERATE_TODOLIST = YES # The GENERATE_TESTLIST tag can be used to enable (YES) or # disable (NO) the test list. This list is created by putting \test # commands in the documentation. GENERATE_TESTLIST = YES # The GENERATE_BUGLIST tag can be used to enable (YES) or # disable (NO) the bug list. This list is created by putting \bug # commands in the documentation. GENERATE_BUGLIST = YES # The GENERATE_DEPRECATEDLIST tag can be used to enable (YES) or # disable (NO) the deprecated list. This list is created by putting # \deprecated commands in the documentation. GENERATE_DEPRECATEDLIST= YES # The ENABLED_SECTIONS tag can be used to enable conditional # documentation sections, marked by \if section-label ... \endif # and \cond section-label ... \endcond blocks. ENABLED_SECTIONS = # The MAX_INITIALIZER_LINES tag determines the maximum number of lines # the initial value of a variable or macro consists of for it to appear in # the documentation. If the initializer consists of more lines than specified # here it will be hidden. Use a value of 0 to hide initializers completely. # The appearance of the initializer of individual variables and macros in the # documentation can be controlled using \showinitializer or \hideinitializer # command in the documentation regardless of this setting. MAX_INITIALIZER_LINES = 30 # Set the SHOW_USED_FILES tag to NO to disable the list of files generated # at the bottom of the documentation of classes and structs. If set to YES the # list will mention the files that were used to generate the documentation. SHOW_USED_FILES = YES # Set the SHOW_FILES tag to NO to disable the generation of the Files page. # This will remove the Files entry from the Quick Index and from the # Folder Tree View (if specified). The default is YES. SHOW_FILES = YES # Set the SHOW_NAMESPACES tag to NO to disable the generation of the # Namespaces page. This will remove the Namespaces entry from the Quick Index # and from the Folder Tree View (if specified). The default is YES. SHOW_NAMESPACES = YES # The FILE_VERSION_FILTER tag can be used to specify a program or script that # doxygen should invoke to get the current version for each file (typically from # the version control system). Doxygen will invoke the program by executing (via # popen()) the command , where is the value of # the FILE_VERSION_FILTER tag, and is the name of an input file # provided by doxygen. Whatever the program writes to standard output # is used as the file version. See the manual for examples. FILE_VERSION_FILTER = # The LAYOUT_FILE tag can be used to specify a layout file which will be parsed # by doxygen. The layout file controls the global structure of the generated # output files in an output format independent way. To create the layout file # that represents doxygen's defaults, run doxygen with the -l option. # You can optionally specify a file name after the option, if omitted # DoxygenLayout.xml will be used as the name of the layout file. LAYOUT_FILE = # The CITE_BIB_FILES tag can be used to specify one or more bib files # containing the references data. This must be a list of .bib files. The # .bib extension is automatically appended if omitted. Using this command # requires the bibtex tool to be installed. See also # http://en.wikipedia.org/wiki/BibTeX for more info. For LaTeX the style # of the bibliography can be controlled using LATEX_BIB_STYLE. To use this # feature you need bibtex and perl available in the search path. Do not use # file names with spaces, bibtex cannot handle them. CITE_BIB_FILES = #--------------------------------------------------------------------------- # configuration options related to warning and progress messages #--------------------------------------------------------------------------- # The QUIET tag can be used to turn on/off the messages that are generated # by doxygen. Possible values are YES and NO. If left blank NO is used. QUIET = NO # The WARNINGS tag can be used to turn on/off the warning messages that are # generated by doxygen. Possible values are YES and NO. If left blank # NO is used. WARNINGS = YES # If WARN_IF_UNDOCUMENTED is set to YES, then doxygen will generate warnings # for undocumented members. If EXTRACT_ALL is set to YES then this flag will # automatically be disabled. WARN_IF_UNDOCUMENTED = YES # If WARN_IF_DOC_ERROR is set to YES, doxygen will generate warnings for # potential errors in the documentation, such as not documenting some # parameters in a documented function, or documenting parameters that # don't exist or using markup commands wrongly. WARN_IF_DOC_ERROR = YES # The WARN_NO_PARAMDOC option can be enabled to get warnings for # functions that are documented, but have no documentation for their parameters # or return value. If set to NO (the default) doxygen will only warn about # wrong or incomplete parameter documentation, but not about the absence of # documentation. WARN_NO_PARAMDOC = NO # The WARN_FORMAT tag determines the format of the warning messages that # doxygen can produce. The string should contain the $file, $line, and $text # tags, which will be replaced by the file and line number from which the # warning originated and the warning text. Optionally the format may contain # $version, which will be replaced by the version of the file (if it could # be obtained via FILE_VERSION_FILTER) WARN_FORMAT = "$file:$line: $text" # The WARN_LOGFILE tag can be used to specify a file to which warning # and error messages should be written. If left blank the output is written # to stderr. WARN_LOGFILE = #--------------------------------------------------------------------------- # configuration options related to the input files #--------------------------------------------------------------------------- # The INPUT tag can be used to specify the files and/or directories that contain # documented source files. You may enter file names like "myfile.cpp" or # directories like "/usr/src/myproject". Separate the files or directories # with spaces. INPUT = # This tag can be used to specify the character encoding of the source files # that doxygen parses. Internally doxygen uses the UTF-8 encoding, which is # also the default input encoding. Doxygen uses libiconv (or the iconv built # into libc) for the transcoding. See http://www.gnu.org/software/libiconv for # the list of possible encodings. INPUT_ENCODING = UTF-8 # If the value of the INPUT tag contains directories, you can use the # FILE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp # and *.h) to filter out the source-files in the directories. If left # blank the following patterns are tested: # *.c *.cc *.cxx *.cpp *.c++ *.d *.java *.ii *.ixx *.ipp *.i++ *.inl *.h *.hh # *.hxx *.hpp *.h++ *.idl *.odl *.cs *.php *.php3 *.inc *.m *.mm *.dox *.py # *.f90 *.f *.for *.vhd *.vhdl FILE_PATTERNS = *.h # The RECURSIVE tag can be used to turn specify whether or not subdirectories # should be searched for input files as well. Possible values are YES and NO. # If left blank NO is used. RECURSIVE = NO # The EXCLUDE tag can be used to specify files and/or directories that should be # excluded from the INPUT source files. This way you can easily exclude a # subdirectory from a directory tree whose root is specified with the INPUT tag. # Note that relative paths are relative to the directory from which doxygen is # run. EXCLUDE = # The EXCLUDE_SYMLINKS tag can be used to select whether or not files or # directories that are symbolic links (a Unix file system feature) are excluded # from the input. EXCLUDE_SYMLINKS = NO # If the value of the INPUT tag contains directories, you can use the # EXCLUDE_PATTERNS tag to specify one or more wildcard patterns to exclude # certain files from those directories. Note that the wildcards are matched # against the file with absolute path, so to exclude all test directories # for example use the pattern */test/* EXCLUDE_PATTERNS = # The EXCLUDE_SYMBOLS tag can be used to specify one or more symbol names # (namespaces, classes, functions, etc.) that should be excluded from the # output. The symbol name can be a fully qualified name, a word, or if the # wildcard * is used, a substring. Examples: ANamespace, AClass, # AClass::ANamespace, ANamespace::*Test EXCLUDE_SYMBOLS = # The EXAMPLE_PATH tag can be used to specify one or more files or # directories that contain example code fragments that are included (see # the \include command). EXAMPLE_PATH = # If the value of the EXAMPLE_PATH tag contains directories, you can use the # EXAMPLE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp # and *.h) to filter out the source-files in the directories. If left # blank all files are included. EXAMPLE_PATTERNS = * # If the EXAMPLE_RECURSIVE tag is set to YES then subdirectories will be # searched for input files to be used with the \include or \dontinclude # commands irrespective of the value of the RECURSIVE tag. # Possible values are YES and NO. If left blank NO is used. EXAMPLE_RECURSIVE = NO # The IMAGE_PATH tag can be used to specify one or more files or # directories that contain image that are included in the documentation (see # the \image command). IMAGE_PATH = # The INPUT_FILTER tag can be used to specify a program that doxygen should # invoke to filter for each input file. Doxygen will invoke the filter program # by executing (via popen()) the command , where # is the value of the INPUT_FILTER tag, and is the name of an # input file. Doxygen will then use the output that the filter program writes # to standard output. If FILTER_PATTERNS is specified, this tag will be # ignored. INPUT_FILTER = # The FILTER_PATTERNS tag can be used to specify filters on a per file pattern # basis. Doxygen will compare the file name with each pattern and apply the # filter if there is a match. The filters are a list of the form: # pattern=filter (like *.cpp=my_cpp_filter). See INPUT_FILTER for further # info on how filters are used. If FILTER_PATTERNS is empty or if # non of the patterns match the file name, INPUT_FILTER is applied. FILTER_PATTERNS = # If the FILTER_SOURCE_FILES tag is set to YES, the input filter (if set using # INPUT_FILTER) will be used to filter the input files when producing source # files to browse (i.e. when SOURCE_BROWSER is set to YES). FILTER_SOURCE_FILES = NO # The FILTER_SOURCE_PATTERNS tag can be used to specify source filters per file # pattern. A pattern will override the setting for FILTER_PATTERN (if any) # and it is also possible to disable source filtering for a specific pattern # using *.ext= (so without naming a filter). This option only has effect when # FILTER_SOURCE_FILES is enabled. FILTER_SOURCE_PATTERNS = # If the USE_MD_FILE_AS_MAINPAGE tag refers to the name of a markdown file that # is part of the input, its contents will be placed on the main page (index.html). # This can be useful if you have a project on for instance GitHub and want reuse # the introduction page also for the doxygen output. USE_MDFILE_AS_MAINPAGE = #--------------------------------------------------------------------------- # configuration options related to source browsing #--------------------------------------------------------------------------- # If the SOURCE_BROWSER tag is set to YES then a list of source files will # be generated. Documented entities will be cross-referenced with these sources. # Note: To get rid of all source code in the generated output, make sure also # VERBATIM_HEADERS is set to NO. SOURCE_BROWSER = NO # Setting the INLINE_SOURCES tag to YES will include the body # of functions and classes directly in the documentation. INLINE_SOURCES = NO # Setting the STRIP_CODE_COMMENTS tag to YES (the default) will instruct # doxygen to hide any special comment blocks from generated source code # fragments. Normal C, C++ and Fortran comments will always remain visible. STRIP_CODE_COMMENTS = YES # If the REFERENCED_BY_RELATION tag is set to YES # then for each documented function all documented # functions referencing it will be listed. REFERENCED_BY_RELATION = NO # If the REFERENCES_RELATION tag is set to YES # then for each documented function all documented entities # called/used by that function will be listed. REFERENCES_RELATION = NO # If the REFERENCES_LINK_SOURCE tag is set to YES (the default) # and SOURCE_BROWSER tag is set to YES, then the hyperlinks from # functions in REFERENCES_RELATION and REFERENCED_BY_RELATION lists will # link to the source code. Otherwise they will link to the documentation. REFERENCES_LINK_SOURCE = YES # If the USE_HTAGS tag is set to YES then the references to source code # will point to the HTML generated by the htags(1) tool instead of doxygen # built-in source browser. The htags tool is part of GNU's global source # tagging system (see http://www.gnu.org/software/global/global.html). You # will need version 4.8.6 or higher. USE_HTAGS = NO # If the VERBATIM_HEADERS tag is set to YES (the default) then Doxygen # will generate a verbatim copy of the header file for each class for # which an include is specified. Set to NO to disable this. VERBATIM_HEADERS = YES #--------------------------------------------------------------------------- # configuration options related to the alphabetical class index #--------------------------------------------------------------------------- # If the ALPHABETICAL_INDEX tag is set to YES, an alphabetical index # of all compounds will be generated. Enable this if the project # contains a lot of classes, structs, unions or interfaces. ALPHABETICAL_INDEX = YES # If the alphabetical index is enabled (see ALPHABETICAL_INDEX) then # the COLS_IN_ALPHA_INDEX tag can be used to specify the number of columns # in which this list will be split (can be a number in the range [1..20]) COLS_IN_ALPHA_INDEX = 5 # In case all classes in a project start with a common prefix, all # classes will be put under the same header in the alphabetical index. # The IGNORE_PREFIX tag can be used to specify one or more prefixes that # should be ignored while generating the index headers. IGNORE_PREFIX = #--------------------------------------------------------------------------- # configuration options related to the HTML output #--------------------------------------------------------------------------- # If the GENERATE_HTML tag is set to YES (the default) Doxygen will # generate HTML output. GENERATE_HTML = YES # The HTML_OUTPUT tag is used to specify where the HTML docs will be put. # If a relative path is entered the value of OUTPUT_DIRECTORY will be # put in front of it. If left blank `html' will be used as the default path. HTML_OUTPUT = html # The HTML_FILE_EXTENSION tag can be used to specify the file extension for # each generated HTML page (for example: .htm,.php,.asp). If it is left blank # doxygen will generate files with .html extension. HTML_FILE_EXTENSION = .html # The HTML_HEADER tag can be used to specify a personal HTML header for # each generated HTML page. If it is left blank doxygen will generate a # standard header. Note that when using a custom header you are responsible # for the proper inclusion of any scripts and style sheets that doxygen # needs, which is dependent on the configuration options used. # It is advised to generate a default header using "doxygen -w html # header.html footer.html stylesheet.css YourConfigFile" and then modify # that header. Note that the header is subject to change so you typically # have to redo this when upgrading to a newer version of doxygen or when # changing the value of configuration settings such as GENERATE_TREEVIEW! HTML_HEADER = # The HTML_FOOTER tag can be used to specify a personal HTML footer for # each generated HTML page. If it is left blank doxygen will generate a # standard footer. HTML_FOOTER = # The HTML_STYLESHEET tag can be used to specify a user-defined cascading # style sheet that is used by each HTML page. It can be used to # fine-tune the look of the HTML output. If left blank doxygen will # generate a default style sheet. Note that it is recommended to use # HTML_EXTRA_STYLESHEET instead of this one, as it is more robust and this # tag will in the future become obsolete. HTML_STYLESHEET = # The HTML_EXTRA_STYLESHEET tag can be used to specify an additional # user-defined cascading style sheet that is included after the standard # style sheets created by doxygen. Using this option one can overrule # certain style aspects. This is preferred over using HTML_STYLESHEET # since it does not replace the standard style sheet and is therefor more # robust against future updates. Doxygen will copy the style sheet file to # the output directory. HTML_EXTRA_STYLESHEET = # The HTML_EXTRA_FILES tag can be used to specify one or more extra images or # other source files which should be copied to the HTML output directory. Note # that these files will be copied to the base HTML output directory. Use the # $relpath$ marker in the HTML_HEADER and/or HTML_FOOTER files to load these # files. In the HTML_STYLESHEET file, use the file name only. Also note that # the files will be copied as-is; there are no commands or markers available. HTML_EXTRA_FILES = # The HTML_COLORSTYLE_HUE tag controls the color of the HTML output. # Doxygen will adjust the colors in the style sheet and background images # according to this color. Hue is specified as an angle on a colorwheel, # see http://en.wikipedia.org/wiki/Hue for more information. # For instance the value 0 represents red, 60 is yellow, 120 is green, # 180 is cyan, 240 is blue, 300 purple, and 360 is red again. # The allowed range is 0 to 359. HTML_COLORSTYLE_HUE = 220 # The HTML_COLORSTYLE_SAT tag controls the purity (or saturation) of # the colors in the HTML output. For a value of 0 the output will use # grayscales only. A value of 255 will produce the most vivid colors. HTML_COLORSTYLE_SAT = 100 # The HTML_COLORSTYLE_GAMMA tag controls the gamma correction applied to # the luminance component of the colors in the HTML output. Values below # 100 gradually make the output lighter, whereas values above 100 make # the output darker. The value divided by 100 is the actual gamma applied, # so 80 represents a gamma of 0.8, The value 220 represents a gamma of 2.2, # and 100 does not change the gamma. HTML_COLORSTYLE_GAMMA = 80 # If the HTML_TIMESTAMP tag is set to YES then the footer of each generated HTML # page will contain the date and time when the page was generated. Setting # this to NO can help when comparing the output of multiple runs. HTML_TIMESTAMP = YES # If the HTML_DYNAMIC_SECTIONS tag is set to YES then the generated HTML # documentation will contain sections that can be hidden and shown after the # page has loaded. HTML_DYNAMIC_SECTIONS = YES # With HTML_INDEX_NUM_ENTRIES one can control the preferred number of # entries shown in the various tree structured indices initially; the user # can expand and collapse entries dynamically later on. Doxygen will expand # the tree to such a level that at most the specified number of entries are # visible (unless a fully collapsed tree already exceeds this amount). # So setting the number of entries 1 will produce a full collapsed tree by # default. 0 is a special value representing an infinite number of entries # and will result in a full expanded tree by default. HTML_INDEX_NUM_ENTRIES = 100 # If the GENERATE_DOCSET tag is set to YES, additional index files # will be generated that can be used as input for Apple's Xcode 3 # integrated development environment, introduced with OSX 10.5 (Leopard). # To create a documentation set, doxygen will generate a Makefile in the # HTML output directory. Running make will produce the docset in that # directory and running "make install" will install the docset in # ~/Library/Developer/Shared/Documentation/DocSets so that Xcode will find # it at startup. # See http://developer.apple.com/tools/creatingdocsetswithdoxygen.html # for more information. GENERATE_DOCSET = NO # When GENERATE_DOCSET tag is set to YES, this tag determines the name of the # feed. A documentation feed provides an umbrella under which multiple # documentation sets from a single provider (such as a company or product suite) # can be grouped. DOCSET_FEEDNAME = "Doxygen generated docs" # When GENERATE_DOCSET tag is set to YES, this tag specifies a string that # should uniquely identify the documentation set bundle. This should be a # reverse domain-name style string, e.g. com.mycompany.MyDocSet. Doxygen # will append .docset to the name. DOCSET_BUNDLE_ID = org.doxygen.Project # When GENERATE_PUBLISHER_ID tag specifies a string that should uniquely # identify the documentation publisher. This should be a reverse domain-name # style string, e.g. com.mycompany.MyDocSet.documentation. DOCSET_PUBLISHER_ID = org.doxygen.Publisher # The GENERATE_PUBLISHER_NAME tag identifies the documentation publisher. DOCSET_PUBLISHER_NAME = Publisher # If the GENERATE_HTMLHELP tag is set to YES, additional index files # will be generated that can be used as input for tools like the # Microsoft HTML help workshop to generate a compiled HTML help file (.chm) # of the generated HTML documentation. GENERATE_HTMLHELP = NO # If the GENERATE_HTMLHELP tag is set to YES, the CHM_FILE tag can # be used to specify the file name of the resulting .chm file. You # can add a path in front of the file if the result should not be # written to the html output directory. CHM_FILE = # If the GENERATE_HTMLHELP tag is set to YES, the HHC_LOCATION tag can # be used to specify the location (absolute path including file name) of # the HTML help compiler (hhc.exe). If non-empty doxygen will try to run # the HTML help compiler on the generated index.hhp. HHC_LOCATION = # If the GENERATE_HTMLHELP tag is set to YES, the GENERATE_CHI flag # controls if a separate .chi index file is generated (YES) or that # it should be included in the master .chm file (NO). GENERATE_CHI = NO # If the GENERATE_HTMLHELP tag is set to YES, the CHM_INDEX_ENCODING # is used to encode HtmlHelp index (hhk), content (hhc) and project file # content. CHM_INDEX_ENCODING = # If the GENERATE_HTMLHELP tag is set to YES, the BINARY_TOC flag # controls whether a binary table of contents is generated (YES) or a # normal table of contents (NO) in the .chm file. BINARY_TOC = NO # The TOC_EXPAND flag can be set to YES to add extra items for group members # to the contents of the HTML help documentation and to the tree view. TOC_EXPAND = NO # If the GENERATE_QHP tag is set to YES and both QHP_NAMESPACE and # QHP_VIRTUAL_FOLDER are set, an additional index file will be generated # that can be used as input for Qt's qhelpgenerator to generate a # Qt Compressed Help (.qch) of the generated HTML documentation. GENERATE_QHP = NO # If the QHG_LOCATION tag is specified, the QCH_FILE tag can # be used to specify the file name of the resulting .qch file. # The path specified is relative to the HTML output folder. QCH_FILE = # The QHP_NAMESPACE tag specifies the namespace to use when generating # Qt Help Project output. For more information please see # http://doc.trolltech.com/qthelpproject.html#namespace QHP_NAMESPACE = org.doxygen.Project # The QHP_VIRTUAL_FOLDER tag specifies the namespace to use when generating # Qt Help Project output. For more information please see # http://doc.trolltech.com/qthelpproject.html#virtual-folders QHP_VIRTUAL_FOLDER = doc # If QHP_CUST_FILTER_NAME is set, it specifies the name of a custom filter to # add. For more information please see # http://doc.trolltech.com/qthelpproject.html#custom-filters QHP_CUST_FILTER_NAME = # The QHP_CUST_FILT_ATTRS tag specifies the list of the attributes of the # custom filter to add. For more information please see # # Qt Help Project / Custom Filters. QHP_CUST_FILTER_ATTRS = # The QHP_SECT_FILTER_ATTRS tag specifies the list of the attributes this # project's # filter section matches. # # Qt Help Project / Filter Attributes. QHP_SECT_FILTER_ATTRS = # If the GENERATE_QHP tag is set to YES, the QHG_LOCATION tag can # be used to specify the location of Qt's qhelpgenerator. # If non-empty doxygen will try to run qhelpgenerator on the generated # .qhp file. QHG_LOCATION = # If the GENERATE_ECLIPSEHELP tag is set to YES, additional index files # will be generated, which together with the HTML files, form an Eclipse help # plugin. To install this plugin and make it available under the help contents # menu in Eclipse, the contents of the directory containing the HTML and XML # files needs to be copied into the plugins directory of eclipse. The name of # the directory within the plugins directory should be the same as # the ECLIPSE_DOC_ID value. After copying Eclipse needs to be restarted before # the help appears. GENERATE_ECLIPSEHELP = NO # A unique identifier for the eclipse help plugin. When installing the plugin # the directory name containing the HTML and XML files should also have # this name. ECLIPSE_DOC_ID = org.doxygen.Project # The DISABLE_INDEX tag can be used to turn on/off the condensed index (tabs) # at top of each HTML page. The value NO (the default) enables the index and # the value YES disables it. Since the tabs have the same information as the # navigation tree you can set this option to NO if you already set # GENERATE_TREEVIEW to YES. DISABLE_INDEX = YES # The GENERATE_TREEVIEW tag is used to specify whether a tree-like index # structure should be generated to display hierarchical information. # If the tag value is set to YES, a side panel will be generated # containing a tree-like index structure (just like the one that # is generated for HTML Help). For this to work a browser that supports # JavaScript, DHTML, CSS and frames is required (i.e. any modern browser). # Windows users are probably better off using the HTML help feature. # Since the tree basically has the same information as the tab index you # could consider to set DISABLE_INDEX to NO when enabling this option. GENERATE_TREEVIEW = NO # The ENUM_VALUES_PER_LINE tag can be used to set the number of enum values # (range [0,1..20]) that doxygen will group on one line in the generated HTML # documentation. Note that a value of 0 will completely suppress the enum # values from appearing in the overview section. ENUM_VALUES_PER_LINE = 4 # If the treeview is enabled (see GENERATE_TREEVIEW) then this tag can be # used to set the initial width (in pixels) of the frame in which the tree # is shown. TREEVIEW_WIDTH = 250 # When the EXT_LINKS_IN_WINDOW option is set to YES doxygen will open # links to external symbols imported via tag files in a separate window. EXT_LINKS_IN_WINDOW = NO # Use this tag to change the font size of Latex formulas included # as images in the HTML documentation. The default is 10. Note that # when you change the font size after a successful doxygen run you need # to manually remove any form_*.png images from the HTML output directory # to force them to be regenerated. FORMULA_FONTSIZE = 10 # Use the FORMULA_TRANPARENT tag to determine whether or not the images # generated for formulas are transparent PNGs. Transparent PNGs are # not supported properly for IE 6.0, but are supported on all modern browsers. # Note that when changing this option you need to delete any form_*.png files # in the HTML output before the changes have effect. FORMULA_TRANSPARENT = YES # Enable the USE_MATHJAX option to render LaTeX formulas using MathJax # (see http://www.mathjax.org) which uses client side Javascript for the # rendering instead of using prerendered bitmaps. Use this if you do not # have LaTeX installed or if you want to formulas look prettier in the HTML # output. When enabled you may also need to install MathJax separately and # configure the path to it using the MATHJAX_RELPATH option. USE_MATHJAX = NO # When MathJax is enabled you can set the default output format to be used for # thA MathJax output. Supported types are HTML-CSS, NativeMML (i.e. MathML) and # SVG. The default value is HTML-CSS, which is slower, but has the best # compatibility. MATHJAX_FORMAT = HTML-CSS # When MathJax is enabled you need to specify the location relative to the # HTML output directory using the MATHJAX_RELPATH option. The destination # directory should contain the MathJax.js script. For instance, if the mathjax # directory is located at the same level as the HTML output directory, then # MATHJAX_RELPATH should be ../mathjax. The default value points to # the MathJax Content Delivery Network so you can quickly see the result without # installing MathJax. However, it is strongly recommended to install a local # copy of MathJax from http://www.mathjax.org before deployment. MATHJAX_RELPATH = http://cdn.mathjax.org/mathjax/latest # The MATHJAX_EXTENSIONS tag can be used to specify one or MathJax extension # names that should be enabled during MathJax rendering. MATHJAX_EXTENSIONS = # When the SEARCHENGINE tag is enabled doxygen will generate a search box # for the HTML output. The underlying search engine uses javascript # and DHTML and should work on any modern browser. Note that when using # HTML help (GENERATE_HTMLHELP), Qt help (GENERATE_QHP), or docsets # (GENERATE_DOCSET) there is already a search function so this one should # typically be disabled. For large projects the javascript based search engine # can be slow, then enabling SERVER_BASED_SEARCH may provide a better solution. SEARCHENGINE = NO # When the SERVER_BASED_SEARCH tag is enabled the search engine will be # implemented using a web server instead of a web client using Javascript. # There are two flavours of web server based search depending on the # EXTERNAL_SEARCH setting. When disabled, doxygen will generate a PHP script for # searching and an index file used by the script. When EXTERNAL_SEARCH is # enabled the indexing and searching needs to be provided by external tools. # See the manual for details. SERVER_BASED_SEARCH = NO # When EXTERNAL_SEARCH is enabled doxygen will no longer generate the PHP # script for searching. Instead the search results are written to an XML file # which needs to be processed by an external indexer. Doxygen will invoke an # external search engine pointed to by the SEARCHENGINE_URL option to obtain # the search results. Doxygen ships with an example indexer (doxyindexer) and # search engine (doxysearch.cgi) which are based on the open source search engine # library Xapian. See the manual for configuration details. EXTERNAL_SEARCH = NO # The SEARCHENGINE_URL should point to a search engine hosted by a web server # which will returned the search results when EXTERNAL_SEARCH is enabled. # Doxygen ships with an example search engine (doxysearch) which is based on # the open source search engine library Xapian. See the manual for configuration # details. SEARCHENGINE_URL = # When SERVER_BASED_SEARCH and EXTERNAL_SEARCH are both enabled the unindexed # search data is written to a file for indexing by an external tool. With the # SEARCHDATA_FILE tag the name of this file can be specified. SEARCHDATA_FILE = searchdata.xml # When SERVER_BASED_SEARCH AND EXTERNAL_SEARCH are both enabled the # EXTERNAL_SEARCH_ID tag can be used as an identifier for the project. This is # useful in combination with EXTRA_SEARCH_MAPPINGS to search through multiple # projects and redirect the results back to the right project. EXTERNAL_SEARCH_ID = # The EXTRA_SEARCH_MAPPINGS tag can be used to enable searching through doxygen # projects other than the one defined by this configuration file, but that are # all added to the same external search index. Each project needs to have a # unique id set via EXTERNAL_SEARCH_ID. The search mapping then maps the id # of to a relative location where the documentation can be found. # The format is: EXTRA_SEARCH_MAPPINGS = id1=loc1 id2=loc2 ... EXTRA_SEARCH_MAPPINGS = #--------------------------------------------------------------------------- # configuration options related to the LaTeX output #--------------------------------------------------------------------------- # If the GENERATE_LATEX tag is set to YES (the default) Doxygen will # generate Latex output. GENERATE_LATEX = NO # The LATEX_OUTPUT tag is used to specify where the LaTeX docs will be put. # If a relative path is entered the value of OUTPUT_DIRECTORY will be # put in front of it. If left blank `latex' will be used as the default path. LATEX_OUTPUT = latex # The LATEX_CMD_NAME tag can be used to specify the LaTeX command name to be # invoked. If left blank `latex' will be used as the default command name. # Note that when enabling USE_PDFLATEX this option is only used for # generating bitmaps for formulas in the HTML output, but not in the # Makefile that is written to the output directory. LATEX_CMD_NAME = latex # The MAKEINDEX_CMD_NAME tag can be used to specify the command name to # generate index for LaTeX. If left blank `makeindex' will be used as the # default command name. MAKEINDEX_CMD_NAME = makeindex # If the COMPACT_LATEX tag is set to YES Doxygen generates more compact # LaTeX documents. This may be useful for small projects and may help to # save some trees in general. COMPACT_LATEX = NO # The PAPER_TYPE tag can be used to set the paper type that is used # by the printer. Possible values are: a4, letter, legal and # executive. If left blank a4wide will be used. PAPER_TYPE = a4 # The EXTRA_PACKAGES tag can be to specify one or more names of LaTeX # packages that should be included in the LaTeX output. EXTRA_PACKAGES = # The LATEX_HEADER tag can be used to specify a personal LaTeX header for # the generated latex document. The header should contain everything until # the first chapter. If it is left blank doxygen will generate a # standard header. Notice: only use this tag if you know what you are doing! LATEX_HEADER = # The LATEX_FOOTER tag can be used to specify a personal LaTeX footer for # the generated latex document. The footer should contain everything after # the last chapter. If it is left blank doxygen will generate a # standard footer. Notice: only use this tag if you know what you are doing! LATEX_FOOTER = # If the PDF_HYPERLINKS tag is set to YES, the LaTeX that is generated # is prepared for conversion to pdf (using ps2pdf). The pdf file will # contain links (just like the HTML output) instead of page references # This makes the output suitable for online browsing using a pdf viewer. PDF_HYPERLINKS = YES # If the USE_PDFLATEX tag is set to YES, pdflatex will be used instead of # plain latex in the generated Makefile. Set this option to YES to get a # higher quality PDF documentation. USE_PDFLATEX = YES # If the LATEX_BATCHMODE tag is set to YES, doxygen will add the \\batchmode. # command to the generated LaTeX files. This will instruct LaTeX to keep # running if errors occur, instead of asking the user for help. # This option is also used when generating formulas in HTML. LATEX_BATCHMODE = NO # If LATEX_HIDE_INDICES is set to YES then doxygen will not # include the index chapters (such as File Index, Compound Index, etc.) # in the output. LATEX_HIDE_INDICES = NO # If LATEX_SOURCE_CODE is set to YES then doxygen will include # source code with syntax highlighting in the LaTeX output. # Note that which sources are shown also depends on other settings # such as SOURCE_BROWSER. LATEX_SOURCE_CODE = NO # The LATEX_BIB_STYLE tag can be used to specify the style to use for the # bibliography, e.g. plainnat, or ieeetr. The default style is "plain". See # http://en.wikipedia.org/wiki/BibTeX for more info. LATEX_BIB_STYLE = plain #--------------------------------------------------------------------------- # configuration options related to the RTF output #--------------------------------------------------------------------------- # If the GENERATE_RTF tag is set to YES Doxygen will generate RTF output # The RTF output is optimized for Word 97 and may not look very pretty with # other RTF readers or editors. GENERATE_RTF = NO # The RTF_OUTPUT tag is used to specify where the RTF docs will be put. # If a relative path is entered the value of OUTPUT_DIRECTORY will be # put in front of it. If left blank `rtf' will be used as the default path. RTF_OUTPUT = rtf # If the COMPACT_RTF tag is set to YES Doxygen generates more compact # RTF documents. This may be useful for small projects and may help to # save some trees in general. COMPACT_RTF = NO # If the RTF_HYPERLINKS tag is set to YES, the RTF that is generated # will contain hyperlink fields. The RTF file will # contain links (just like the HTML output) instead of page references. # This makes the output suitable for online browsing using WORD or other # programs which support those fields. # Note: wordpad (write) and others do not support links. RTF_HYPERLINKS = NO # Load style sheet definitions from file. Syntax is similar to doxygen's # config file, i.e. a series of assignments. You only have to provide # replacements, missing definitions are set to their default value. RTF_STYLESHEET_FILE = # Set optional variables used in the generation of an rtf document. # Syntax is similar to doxygen's config file. RTF_EXTENSIONS_FILE = #--------------------------------------------------------------------------- # configuration options related to the man page output #--------------------------------------------------------------------------- # If the GENERATE_MAN tag is set to YES (the default) Doxygen will # generate man pages GENERATE_MAN = YES # The MAN_OUTPUT tag is used to specify where the man pages will be put. # If a relative path is entered the value of OUTPUT_DIRECTORY will be # put in front of it. If left blank `man' will be used as the default path. MAN_OUTPUT = man # The MAN_EXTENSION tag determines the extension that is added to # the generated man pages (default is the subroutine's section .3) MAN_EXTENSION = .3 # If the MAN_LINKS tag is set to YES and Doxygen generates man output, # then it will generate one additional man file for each entity # documented in the real man page(s). These additional files # only source the real man page, but without them the man command # would be unable to find the correct page. The default is NO. MAN_LINKS = YES #--------------------------------------------------------------------------- # configuration options related to the XML output #--------------------------------------------------------------------------- # If the GENERATE_XML tag is set to YES Doxygen will # generate an XML file that captures the structure of # the code including all documentation. GENERATE_XML = NO # The XML_OUTPUT tag is used to specify where the XML pages will be put. # If a relative path is entered the value of OUTPUT_DIRECTORY will be # put in front of it. If left blank `xml' will be used as the default path. XML_OUTPUT = xml # The XML_SCHEMA tag can be used to specify an XML schema, # which can be used by a validating XML parser to check the # syntax of the XML files. XML_SCHEMA = # The XML_DTD tag can be used to specify an XML DTD, # which can be used by a validating XML parser to check the # syntax of the XML files. XML_DTD = # If the XML_PROGRAMLISTING tag is set to YES Doxygen will # dump the program listings (including syntax highlighting # and cross-referencing information) to the XML output. Note that # enabling this will significantly increase the size of the XML output. XML_PROGRAMLISTING = YES #--------------------------------------------------------------------------- # configuration options for the AutoGen Definitions output #--------------------------------------------------------------------------- # If the GENERATE_AUTOGEN_DEF tag is set to YES Doxygen will # generate an AutoGen Definitions (see autogen.sf.net) file # that captures the structure of the code including all # documentation. Note that this feature is still experimental # and incomplete at the moment. GENERATE_AUTOGEN_DEF = NO #--------------------------------------------------------------------------- # configuration options related to the Perl module output #--------------------------------------------------------------------------- # If the GENERATE_PERLMOD tag is set to YES Doxygen will # generate a Perl module file that captures the structure of # the code including all documentation. Note that this # feature is still experimental and incomplete at the # moment. GENERATE_PERLMOD = NO # If the PERLMOD_LATEX tag is set to YES Doxygen will generate # the necessary Makefile rules, Perl scripts and LaTeX code to be able # to generate PDF and DVI output from the Perl module output. PERLMOD_LATEX = NO # If the PERLMOD_PRETTY tag is set to YES the Perl module output will be # nicely formatted so it can be parsed by a human reader. This is useful # if you want to understand what is going on. On the other hand, if this # tag is set to NO the size of the Perl module output will be much smaller # and Perl will parse it just the same. PERLMOD_PRETTY = YES # The names of the make variables in the generated doxyrules.make file # are prefixed with the string contained in PERLMOD_MAKEVAR_PREFIX. # This is useful so different doxyrules.make files included by the same # Makefile don't overwrite each other's variables. PERLMOD_MAKEVAR_PREFIX = #--------------------------------------------------------------------------- # Configuration options related to the preprocessor #--------------------------------------------------------------------------- # If the ENABLE_PREPROCESSING tag is set to YES (the default) Doxygen will # evaluate all C-preprocessor directives found in the sources and include # files. ENABLE_PREPROCESSING = YES # If the MACRO_EXPANSION tag is set to YES Doxygen will expand all macro # names in the source code. If set to NO (the default) only conditional # compilation will be performed. Macro expansion can be done in a controlled # way by setting EXPAND_ONLY_PREDEF to YES. MACRO_EXPANSION = NO # If the EXPAND_ONLY_PREDEF and MACRO_EXPANSION tags are both set to YES # then the macro expansion is limited to the macros specified with the # PREDEFINED and EXPAND_AS_DEFINED tags. EXPAND_ONLY_PREDEF = NO # If the SEARCH_INCLUDES tag is set to YES (the default) the includes files # pointed to by INCLUDE_PATH will be searched when a #include is found. SEARCH_INCLUDES = YES # The INCLUDE_PATH tag can be used to specify one or more directories that # contain include files that are not input files but should be processed by # the preprocessor. INCLUDE_PATH = # You can use the INCLUDE_FILE_PATTERNS tag to specify one or more wildcard # patterns (like *.h and *.hpp) to filter out the header-files in the # directories. If left blank, the patterns specified with FILE_PATTERNS will # be used. INCLUDE_FILE_PATTERNS = # The PREDEFINED tag can be used to specify one or more macro names that # are defined before the preprocessor is started (similar to the -D option of # gcc). The argument of the tag is a list of macros of the form: name # or name=definition (no spaces). If the definition and the = are # omitted =1 is assumed. To prevent a macro definition from being # undefined via #undef or recursively expanded use the := operator # instead of the = operator. PREDEFINED = # If the MACRO_EXPANSION and EXPAND_ONLY_PREDEF tags are set to YES then # this tag can be used to specify a list of macro names that should be expanded. # The macro definition that is found in the sources will be used. # Use the PREDEFINED tag if you want to use a different macro definition that # overrules the definition found in the source code. EXPAND_AS_DEFINED = # If the SKIP_FUNCTION_MACROS tag is set to YES (the default) then # doxygen's preprocessor will remove all references to function-like macros # that are alone on a line, have an all uppercase name, and do not end with a # semicolon, because these will confuse the parser if not removed. SKIP_FUNCTION_MACROS = YES #--------------------------------------------------------------------------- # Configuration::additions related to external references #--------------------------------------------------------------------------- # The TAGFILES option can be used to specify one or more tagfiles. For each # tag file the location of the external documentation should be added. The # format of a tag file without this location is as follows: # TAGFILES = file1 file2 ... # Adding location for the tag files is done as follows: # TAGFILES = file1=loc1 "file2 = loc2" ... # where "loc1" and "loc2" can be relative or absolute paths # or URLs. Note that each tag file must have a unique name (where the name does # NOT include the path). If a tag file is not located in the directory in which # doxygen is run, you must also specify the path to the tagfile here. TAGFILES = # When a file name is specified after GENERATE_TAGFILE, doxygen will create # a tag file that is based on the input files it reads. GENERATE_TAGFILE = # If the ALLEXTERNALS tag is set to YES all external classes will be listed # in the class index. If set to NO only the inherited external classes # will be listed. ALLEXTERNALS = NO # If the EXTERNAL_GROUPS tag is set to YES all external groups will be listed # in the modules index. If set to NO, only the current project's groups will # be listed. EXTERNAL_GROUPS = YES # The PERL_PATH should be the absolute path and name of the perl script # interpreter (i.e. the result of `which perl'). PERL_PATH = /usr/bin/perl #--------------------------------------------------------------------------- # Configuration options related to the dot tool #--------------------------------------------------------------------------- # If the CLASS_DIAGRAMS tag is set to YES (the default) Doxygen will # generate a inheritance diagram (in HTML, RTF and LaTeX) for classes with base # or super classes. Setting the tag to NO turns the diagrams off. Note that # this option also works with HAVE_DOT disabled, but it is recommended to # install and use dot, since it yields more powerful graphs. CLASS_DIAGRAMS = NO # You can define message sequence charts within doxygen comments using the \msc # command. Doxygen will then run the mscgen tool (see # http://www.mcternan.me.uk/mscgen/) to produce the chart and insert it in the # documentation. The MSCGEN_PATH tag allows you to specify the directory where # the mscgen tool resides. If left empty the tool is assumed to be found in the # default search path. MSCGEN_PATH = # If set to YES, the inheritance and collaboration graphs will hide # inheritance and usage relations if the target is undocumented # or is not a class. HIDE_UNDOC_RELATIONS = YES # If you set the HAVE_DOT tag to YES then doxygen will assume the dot tool is # available from the path. This tool is part of Graphviz, a graph visualization # toolkit from AT&T and Lucent Bell Labs. The other options in this section # have no effect if this option is set to NO (the default) HAVE_DOT = NO # The DOT_NUM_THREADS specifies the number of dot invocations doxygen is # allowed to run in parallel. When set to 0 (the default) doxygen will # base this on the number of processors available in the system. You can set it # explicitly to a value larger than 0 to get control over the balance # between CPU load and processing speed. DOT_NUM_THREADS = 0 # By default doxygen will use the Helvetica font for all dot files that # doxygen generates. When you want a differently looking font you can specify # the font name using DOT_FONTNAME. You need to make sure dot is able to find # the font, which can be done by putting it in a standard location or by setting # the DOTFONTPATH environment variable or by setting DOT_FONTPATH to the # directory containing the font. DOT_FONTNAME = Helvetica # The DOT_FONTSIZE tag can be used to set the size of the font of dot graphs. # The default size is 10pt. DOT_FONTSIZE = 10 # By default doxygen will tell dot to use the Helvetica font. # If you specify a different font using DOT_FONTNAME you can use DOT_FONTPATH to # set the path where dot can find it. DOT_FONTPATH = # If the CLASS_GRAPH and HAVE_DOT tags are set to YES then doxygen # will generate a graph for each documented class showing the direct and # indirect inheritance relations. Setting this tag to YES will force the # CLASS_DIAGRAMS tag to NO. CLASS_GRAPH = YES # If the COLLABORATION_GRAPH and HAVE_DOT tags are set to YES then doxygen # will generate a graph for each documented class showing the direct and # indirect implementation dependencies (inheritance, containment, and # class references variables) of the class with other documented classes. COLLABORATION_GRAPH = YES # If the GROUP_GRAPHS and HAVE_DOT tags are set to YES then doxygen # will generate a graph for groups, showing the direct groups dependencies GROUP_GRAPHS = YES # If the UML_LOOK tag is set to YES doxygen will generate inheritance and # collaboration diagrams in a style similar to the OMG's Unified Modeling # Language. UML_LOOK = NO # If the UML_LOOK tag is enabled, the fields and methods are shown inside # the class node. If there are many fields or methods and many nodes the # graph may become too big to be useful. The UML_LIMIT_NUM_FIELDS # threshold limits the number of items for each type to make the size more # managable. Set this to 0 for no limit. Note that the threshold may be # exceeded by 50% before the limit is enforced. UML_LIMIT_NUM_FIELDS = 10 # If set to YES, the inheritance and collaboration graphs will show the # relations between templates and their instances. TEMPLATE_RELATIONS = NO # If the ENABLE_PREPROCESSING, SEARCH_INCLUDES, INCLUDE_GRAPH, and HAVE_DOT # tags are set to YES then doxygen will generate a graph for each documented # file showing the direct and indirect include dependencies of the file with # other documented files. INCLUDE_GRAPH = YES # If the ENABLE_PREPROCESSING, SEARCH_INCLUDES, INCLUDED_BY_GRAPH, and # HAVE_DOT tags are set to YES then doxygen will generate a graph for each # documented header file showing the documented files that directly or # indirectly include this file. INCLUDED_BY_GRAPH = YES # If the CALL_GRAPH and HAVE_DOT options are set to YES then # doxygen will generate a call dependency graph for every global function # or class method. Note that enabling this option will significantly increase # the time of a run. So in most cases it will be better to enable call graphs # for selected functions only using the \callgraph command. CALL_GRAPH = NO # If the CALLER_GRAPH and HAVE_DOT tags are set to YES then # doxygen will generate a caller dependency graph for every global function # or class method. Note that enabling this option will significantly increase # the time of a run. So in most cases it will be better to enable caller # graphs for selected functions only using the \callergraph command. CALLER_GRAPH = NO # If the GRAPHICAL_HIERARCHY and HAVE_DOT tags are set to YES then doxygen # will generate a graphical hierarchy of all classes instead of a textual one. GRAPHICAL_HIERARCHY = YES # If the DIRECTORY_GRAPH and HAVE_DOT tags are set to YES # then doxygen will show the dependencies a directory has on other directories # in a graphical way. The dependency relations are determined by the #include # relations between the files in the directories. DIRECTORY_GRAPH = YES # The DOT_IMAGE_FORMAT tag can be used to set the image format of the images # generated by dot. Possible values are svg, png, jpg, or gif. # If left blank png will be used. If you choose svg you need to set # HTML_FILE_EXTENSION to xhtml in order to make the SVG files # visible in IE 9+ (other browsers do not have this requirement). DOT_IMAGE_FORMAT = png # If DOT_IMAGE_FORMAT is set to svg, then this option can be set to YES to # enable generation of interactive SVG images that allow zooming and panning. # Note that this requires a modern browser other than Internet Explorer. # Tested and working are Firefox, Chrome, Safari, and Opera. For IE 9+ you # need to set HTML_FILE_EXTENSION to xhtml in order to make the SVG files # visible. Older versions of IE do not have SVG support. INTERACTIVE_SVG = NO # The tag DOT_PATH can be used to specify the path where the dot tool can be # found. If left blank, it is assumed the dot tool can be found in the path. DOT_PATH = # The DOTFILE_DIRS tag can be used to specify one or more directories that # contain dot files that are included in the documentation (see the # \dotfile command). DOTFILE_DIRS = # The MSCFILE_DIRS tag can be used to specify one or more directories that # contain msc files that are included in the documentation (see the # \mscfile command). MSCFILE_DIRS = # The DOT_GRAPH_MAX_NODES tag can be used to set the maximum number of # nodes that will be shown in the graph. If the number of nodes in a graph # becomes larger than this value, doxygen will truncate the graph, which is # visualized by representing a node as a red box. Note that doxygen if the # number of direct children of the root node in a graph is already larger than # DOT_GRAPH_MAX_NODES then the graph will not be shown at all. Also note # that the size of a graph can be further restricted by MAX_DOT_GRAPH_DEPTH. DOT_GRAPH_MAX_NODES = 50 # The MAX_DOT_GRAPH_DEPTH tag can be used to set the maximum depth of the # graphs generated by dot. A depth value of 3 means that only nodes reachable # from the root by following a path via at most 3 edges will be shown. Nodes # that lay further from the root node will be omitted. Note that setting this # option to 1 or 2 may greatly reduce the computation time needed for large # code bases. Also note that the size of a graph can be further restricted by # DOT_GRAPH_MAX_NODES. Using a depth of 0 means no depth restriction. MAX_DOT_GRAPH_DEPTH = 0 # Set the DOT_TRANSPARENT tag to YES to generate images with a transparent # background. This is disabled by default, because dot on Windows does not # seem to support this out of the box. Warning: Depending on the platform used, # enabling this option may lead to badly anti-aliased labels on the edges of # a graph (i.e. they become hard to read). DOT_TRANSPARENT = NO # Set the DOT_MULTI_TARGETS tag to YES allow dot to generate multiple output # files in one run (i.e. multiple -o and -T options on the command line). This # makes dot run faster, but since only newer versions of dot (>1.8.10) # support this, this feature is disabled by default. DOT_MULTI_TARGETS = NO # If the GENERATE_LEGEND tag is set to YES (the default) Doxygen will # generate a legend page explaining the meaning of the various boxes and # arrows in the dot generated graphs. GENERATE_LEGEND = YES # If the DOT_CLEANUP tag is set to YES (the default) Doxygen will # remove the intermediate dot files that are used to generate # the various graphs. DOT_CLEANUP = YES pacparser-1.4.5/docs/html/000077500000000000000000000000001464010763600154045ustar00rootroot00000000000000pacparser-1.4.5/docs/html/doxygen.css000066400000000000000000000614231464010763600176010ustar00rootroot00000000000000/* The standard CSS for doxygen 1.8.8 */ body, table, div, p, dl { font: 400 14px/22px Roboto,sans-serif; } /* @group Heading Levels */ h1.groupheader { font-size: 150%; } .title { font: 400 14px/28px Roboto,sans-serif; font-size: 150%; font-weight: bold; margin: 10px 2px; } h2.groupheader { border-bottom: 1px solid #879ECB; color: #354C7B; font-size: 150%; font-weight: normal; margin-top: 1.75em; padding-top: 8px; padding-bottom: 4px; width: 100%; } h3.groupheader { font-size: 100%; } h1, h2, h3, h4, h5, h6 { -webkit-transition: text-shadow 0.5s linear; -moz-transition: text-shadow 0.5s linear; -ms-transition: text-shadow 0.5s linear; -o-transition: text-shadow 0.5s linear; transition: text-shadow 0.5s linear; margin-right: 15px; } h1.glow, h2.glow, h3.glow, h4.glow, h5.glow, h6.glow { text-shadow: 0 0 15px cyan; } dt { font-weight: bold; } div.multicol { -moz-column-gap: 1em; -webkit-column-gap: 1em; -moz-column-count: 3; -webkit-column-count: 3; } p.startli, p.startdd { margin-top: 2px; } p.starttd { margin-top: 0px; } p.endli { margin-bottom: 0px; } p.enddd { margin-bottom: 4px; } p.endtd { margin-bottom: 2px; } /* @end */ caption { font-weight: bold; } span.legend { font-size: 70%; text-align: center; } h3.version { font-size: 90%; text-align: center; } div.qindex, div.navtab{ background-color: #EBEFF6; border: 1px solid #A3B4D7; text-align: center; } div.qindex, div.navpath { width: 100%; line-height: 140%; } div.navtab { margin-right: 15px; } /* @group Link Styling */ a { color: #3D578C; font-weight: normal; text-decoration: none; } .contents a:visited { color: #4665A2; } a:hover { text-decoration: underline; } a.qindex { font-weight: bold; } a.qindexHL { font-weight: bold; background-color: #9CAFD4; color: #ffffff; border: 1px double #869DCA; } .contents a.qindexHL:visited { color: #ffffff; } a.el { font-weight: bold; } a.elRef { } a.code, a.code:visited, a.line, a.line:visited { color: #4665A2; } a.codeRef, a.codeRef:visited, a.lineRef, a.lineRef:visited { color: #4665A2; } /* @end */ dl.el { margin-left: -1cm; } pre.fragment { border: 1px solid #C4CFE5; background-color: #FBFCFD; padding: 4px 6px; margin: 4px 8px 4px 2px; overflow: auto; word-wrap: break-word; font-size: 9pt; line-height: 125%; font-family: monospace, fixed; font-size: 105%; } div.fragment { padding: 4px 6px; margin: 4px 8px 4px 2px; background-color: #FBFCFD; border: 1px solid #C4CFE5; } div.line { font-family: monospace, fixed; font-size: 13px; min-height: 13px; line-height: 1.0; text-wrap: unrestricted; white-space: -moz-pre-wrap; /* Moz */ white-space: -pre-wrap; /* Opera 4-6 */ white-space: -o-pre-wrap; /* Opera 7 */ white-space: pre-wrap; /* CSS3 */ word-wrap: break-word; /* IE 5.5+ */ text-indent: -53px; padding-left: 53px; padding-bottom: 0px; margin: 0px; -webkit-transition-property: background-color, box-shadow; -webkit-transition-duration: 0.5s; -moz-transition-property: background-color, box-shadow; -moz-transition-duration: 0.5s; -ms-transition-property: background-color, box-shadow; -ms-transition-duration: 0.5s; -o-transition-property: background-color, box-shadow; -o-transition-duration: 0.5s; transition-property: background-color, box-shadow; transition-duration: 0.5s; } div.line.glow { background-color: cyan; box-shadow: 0 0 10px cyan; } span.lineno { padding-right: 4px; text-align: right; border-right: 2px solid #0F0; background-color: #E8E8E8; white-space: pre; } span.lineno a { background-color: #D8D8D8; } span.lineno a:hover { background-color: #C8C8C8; } div.ah { background-color: black; font-weight: bold; color: #ffffff; margin-bottom: 3px; margin-top: 3px; padding: 0.2em; border: solid thin #333; border-radius: 0.5em; -webkit-border-radius: .5em; -moz-border-radius: .5em; box-shadow: 2px 2px 3px #999; -webkit-box-shadow: 2px 2px 3px #999; -moz-box-shadow: rgba(0, 0, 0, 0.15) 2px 2px 2px; background-image: -webkit-gradient(linear, left top, left bottom, from(#eee), to(#000),color-stop(0.3, #444)); background-image: -moz-linear-gradient(center top, #eee 0%, #444 40%, #000); } div.groupHeader { margin-left: 16px; margin-top: 12px; font-weight: bold; } div.groupText { margin-left: 16px; font-style: italic; } body { background-color: white; color: black; margin: 0; } div.contents { margin-top: 10px; margin-left: 12px; margin-right: 8px; } td.indexkey { background-color: #EBEFF6; font-weight: bold; border: 1px solid #C4CFE5; margin: 2px 0px 2px 0; padding: 2px 10px; white-space: nowrap; vertical-align: top; } td.indexvalue { background-color: #EBEFF6; border: 1px solid #C4CFE5; padding: 2px 10px; margin: 2px 0px; } tr.memlist { background-color: #EEF1F7; } p.formulaDsp { text-align: center; } img.formulaDsp { } img.formulaInl { vertical-align: middle; } div.center { text-align: center; margin-top: 0px; margin-bottom: 0px; padding: 0px; } div.center img { border: 0px; } address.footer { text-align: right; padding-right: 12px; } img.footer { border: 0px; vertical-align: middle; } /* @group Code Colorization */ span.keyword { color: #008000 } span.keywordtype { color: #604020 } span.keywordflow { color: #e08000 } span.comment { color: #800000 } span.preprocessor { color: #806020 } span.stringliteral { color: #002080 } span.charliteral { color: #008080 } span.vhdldigit { color: #ff00ff } span.vhdlchar { color: #000000 } span.vhdlkeyword { color: #700070 } span.vhdllogic { color: #ff0000 } blockquote { background-color: #F7F8FB; border-left: 2px solid #9CAFD4; margin: 0 24px 0 4px; padding: 0 12px 0 16px; } /* @end */ /* .search { color: #003399; font-weight: bold; } form.search { margin-bottom: 0px; margin-top: 0px; } input.search { font-size: 75%; color: #000080; font-weight: normal; background-color: #e8eef2; } */ td.tiny { font-size: 75%; } .dirtab { padding: 4px; border-collapse: collapse; border: 1px solid #A3B4D7; } th.dirtab { background: #EBEFF6; font-weight: bold; } hr { height: 0px; border: none; border-top: 1px solid #4A6AAA; } hr.footer { height: 1px; } /* @group Member Descriptions */ table.memberdecls { border-spacing: 0px; padding: 0px; } .memberdecls td, .fieldtable tr { -webkit-transition-property: background-color, box-shadow; -webkit-transition-duration: 0.5s; -moz-transition-property: background-color, box-shadow; -moz-transition-duration: 0.5s; -ms-transition-property: background-color, box-shadow; -ms-transition-duration: 0.5s; -o-transition-property: background-color, box-shadow; -o-transition-duration: 0.5s; transition-property: background-color, box-shadow; transition-duration: 0.5s; } .memberdecls td.glow, .fieldtable tr.glow { background-color: cyan; box-shadow: 0 0 15px cyan; } .mdescLeft, .mdescRight, .memItemLeft, .memItemRight, .memTemplItemLeft, .memTemplItemRight, .memTemplParams { background-color: #F9FAFC; border: none; margin: 4px; padding: 1px 0 0 8px; } .mdescLeft, .mdescRight { padding: 0px 8px 4px 8px; color: #555; } .memSeparator { border-bottom: 1px solid #DEE4F0; line-height: 1px; margin: 0px; padding: 0px; } .memItemLeft, .memTemplItemLeft { white-space: nowrap; } .memItemRight { width: 100%; } .memTemplParams { color: #4665A2; white-space: nowrap; font-size: 80%; } /* @end */ /* @group Member Details */ /* Styles for detailed member documentation */ .memtemplate { font-size: 80%; color: #4665A2; font-weight: normal; margin-left: 9px; } .memnav { background-color: #EBEFF6; border: 1px solid #A3B4D7; text-align: center; margin: 2px; margin-right: 15px; padding: 2px; } .mempage { width: 100%; } .memitem { padding: 0; margin-bottom: 10px; margin-right: 5px; -webkit-transition: box-shadow 0.5s linear; -moz-transition: box-shadow 0.5s linear; -ms-transition: box-shadow 0.5s linear; -o-transition: box-shadow 0.5s linear; transition: box-shadow 0.5s linear; display: table !important; width: 100%; } .memitem.glow { box-shadow: 0 0 15px cyan; } .memname { font-weight: bold; margin-left: 6px; } .memname td { vertical-align: bottom; } .memproto, dl.reflist dt { border-top: 1px solid #A8B8D9; border-left: 1px solid #A8B8D9; border-right: 1px solid #A8B8D9; padding: 6px 0px 6px 0px; color: #253555; font-weight: bold; text-shadow: 0px 1px 1px rgba(255, 255, 255, 0.9); background-image:url('nav_f.png'); background-repeat:repeat-x; background-color: #E2E8F2; /* opera specific markup */ box-shadow: 5px 5px 5px rgba(0, 0, 0, 0.15); border-top-right-radius: 4px; border-top-left-radius: 4px; /* firefox specific markup */ -moz-box-shadow: rgba(0, 0, 0, 0.15) 5px 5px 5px; -moz-border-radius-topright: 4px; -moz-border-radius-topleft: 4px; /* webkit specific markup */ -webkit-box-shadow: 5px 5px 5px rgba(0, 0, 0, 0.15); -webkit-border-top-right-radius: 4px; -webkit-border-top-left-radius: 4px; } .memdoc, dl.reflist dd { border-bottom: 1px solid #A8B8D9; border-left: 1px solid #A8B8D9; border-right: 1px solid #A8B8D9; padding: 6px 10px 2px 10px; background-color: #FBFCFD; border-top-width: 0; background-image:url('nav_g.png'); background-repeat:repeat-x; background-color: #FFFFFF; /* opera specific markup */ border-bottom-left-radius: 4px; border-bottom-right-radius: 4px; box-shadow: 5px 5px 5px rgba(0, 0, 0, 0.15); /* firefox specific markup */ -moz-border-radius-bottomleft: 4px; -moz-border-radius-bottomright: 4px; -moz-box-shadow: rgba(0, 0, 0, 0.15) 5px 5px 5px; /* webkit specific markup */ -webkit-border-bottom-left-radius: 4px; -webkit-border-bottom-right-radius: 4px; -webkit-box-shadow: 5px 5px 5px rgba(0, 0, 0, 0.15); } dl.reflist dt { padding: 5px; } dl.reflist dd { margin: 0px 0px 10px 0px; padding: 5px; } .paramkey { text-align: right; } .paramtype { white-space: nowrap; } .paramname { color: #602020; white-space: nowrap; } .paramname em { font-style: normal; } .paramname code { line-height: 14px; } .params, .retval, .exception, .tparams { margin-left: 0px; padding-left: 0px; } .params .paramname, .retval .paramname { font-weight: bold; vertical-align: top; } .params .paramtype { font-style: italic; vertical-align: top; } .params .paramdir { font-family: "courier new",courier,monospace; vertical-align: top; } table.mlabels { border-spacing: 0px; } td.mlabels-left { width: 100%; padding: 0px; } td.mlabels-right { vertical-align: bottom; padding: 0px; white-space: nowrap; } span.mlabels { margin-left: 8px; } span.mlabel { background-color: #728DC1; border-top:1px solid #5373B4; border-left:1px solid #5373B4; border-right:1px solid #C4CFE5; border-bottom:1px solid #C4CFE5; text-shadow: none; color: white; margin-right: 4px; padding: 2px 3px; border-radius: 3px; font-size: 7pt; white-space: nowrap; vertical-align: middle; } /* @end */ /* these are for tree view inside a (index) page */ div.directory { margin: 10px 0px; border-top: 1px solid #9CAFD4; border-bottom: 1px solid #9CAFD4; width: 100%; } .directory table { border-collapse:collapse; } .directory td { margin: 0px; padding: 0px; vertical-align: top; } .directory td.entry { white-space: nowrap; padding-right: 6px; padding-top: 3px; } .directory td.entry a { outline:none; } .directory td.entry a img { border: none; } .directory td.desc { width: 100%; padding-left: 6px; padding-right: 6px; padding-top: 3px; border-left: 1px solid rgba(0,0,0,0.05); } .directory tr.even { padding-left: 6px; background-color: #F7F8FB; } .directory img { vertical-align: -30%; } .directory .levels { white-space: nowrap; width: 100%; text-align: right; font-size: 9pt; } .directory .levels span { cursor: pointer; padding-left: 2px; padding-right: 2px; color: #3D578C; } .arrow { color: #9CAFD4; -webkit-user-select: none; -khtml-user-select: none; -moz-user-select: none; -ms-user-select: none; user-select: none; cursor: pointer; font-size: 80%; display: inline-block; width: 16px; height: 22px; } .icon { font-family: Arial, Helvetica; font-weight: bold; font-size: 12px; height: 14px; width: 16px; display: inline-block; background-color: #728DC1; color: white; text-align: center; border-radius: 4px; margin-left: 2px; margin-right: 2px; } .icona { width: 24px; height: 22px; display: inline-block; } .iconfopen { width: 24px; height: 18px; margin-bottom: 4px; background-image:url('ftv2folderopen.png'); background-position: 0px -4px; background-repeat: repeat-y; vertical-align:top; display: inline-block; } .iconfclosed { width: 24px; height: 18px; margin-bottom: 4px; background-image:url('ftv2folderclosed.png'); background-position: 0px -4px; background-repeat: repeat-y; vertical-align:top; display: inline-block; } .icondoc { width: 24px; height: 18px; margin-bottom: 4px; background-image:url('ftv2doc.png'); background-position: 0px -4px; background-repeat: repeat-y; vertical-align:top; display: inline-block; } table.directory { font: 400 14px Roboto,sans-serif; } /* @end */ div.dynheader { margin-top: 8px; -webkit-touch-callout: none; -webkit-user-select: none; -khtml-user-select: none; -moz-user-select: none; -ms-user-select: none; user-select: none; } address { font-style: normal; color: #2A3D61; } table.doxtable { border-collapse:collapse; margin-top: 4px; margin-bottom: 4px; } table.doxtable td, table.doxtable th { border: 1px solid #2D4068; padding: 3px 7px 2px; } table.doxtable th { background-color: #374F7F; color: #FFFFFF; font-size: 110%; padding-bottom: 4px; padding-top: 5px; } table.fieldtable { /*width: 100%;*/ margin-bottom: 10px; border: 1px solid #A8B8D9; border-spacing: 0px; -moz-border-radius: 4px; -webkit-border-radius: 4px; border-radius: 4px; -moz-box-shadow: rgba(0, 0, 0, 0.15) 2px 2px 2px; -webkit-box-shadow: 2px 2px 2px rgba(0, 0, 0, 0.15); box-shadow: 2px 2px 2px rgba(0, 0, 0, 0.15); } .fieldtable td, .fieldtable th { padding: 3px 7px 2px; } .fieldtable td.fieldtype, .fieldtable td.fieldname { white-space: nowrap; border-right: 1px solid #A8B8D9; border-bottom: 1px solid #A8B8D9; vertical-align: top; } .fieldtable td.fieldname { padding-top: 3px; } .fieldtable td.fielddoc { border-bottom: 1px solid #A8B8D9; /*width: 100%;*/ } .fieldtable td.fielddoc p:first-child { margin-top: 0px; } .fieldtable td.fielddoc p:last-child { margin-bottom: 2px; } .fieldtable tr:last-child td { border-bottom: none; } .fieldtable th { background-image:url('nav_f.png'); background-repeat:repeat-x; background-color: #E2E8F2; font-size: 90%; color: #253555; padding-bottom: 4px; padding-top: 5px; text-align:left; -moz-border-radius-topleft: 4px; -moz-border-radius-topright: 4px; -webkit-border-top-left-radius: 4px; -webkit-border-top-right-radius: 4px; border-top-left-radius: 4px; border-top-right-radius: 4px; border-bottom: 1px solid #A8B8D9; } .tabsearch { top: 0px; left: 10px; height: 36px; background-image: url('tab_b.png'); z-index: 101; overflow: hidden; font-size: 13px; } .navpath ul { font-size: 11px; background-image:url('tab_b.png'); background-repeat:repeat-x; background-position: 0 -5px; height:30px; line-height:30px; color:#8AA0CC; border:solid 1px #C2CDE4; overflow:hidden; margin:0px; padding:0px; } .navpath li { list-style-type:none; float:left; padding-left:10px; padding-right:15px; background-image:url('bc_s.png'); background-repeat:no-repeat; background-position:right; color:#364D7C; } .navpath li.navelem a { height:32px; display:block; text-decoration: none; outline: none; color: #283A5D; font-family: 'Lucida Grande',Geneva,Helvetica,Arial,sans-serif; text-shadow: 0px 1px 1px rgba(255, 255, 255, 0.9); text-decoration: none; } .navpath li.navelem a:hover { color:#6884BD; } .navpath li.footer { list-style-type:none; float:right; padding-left:10px; padding-right:15px; background-image:none; background-repeat:no-repeat; background-position:right; color:#364D7C; font-size: 8pt; } div.summary { float: right; font-size: 8pt; padding-right: 5px; width: 50%; text-align: right; } div.summary a { white-space: nowrap; } div.ingroups { font-size: 8pt; width: 50%; text-align: left; } div.ingroups a { white-space: nowrap; } div.header { background-image:url('nav_h.png'); background-repeat:repeat-x; background-color: #F9FAFC; margin: 0px; border-bottom: 1px solid #C4CFE5; } div.headertitle { padding: 5px 5px 5px 10px; } dl { padding: 0 0 0 10px; } /* dl.note, dl.warning, dl.attention, dl.pre, dl.post, dl.invariant, dl.deprecated, dl.todo, dl.test, dl.bug */ dl.section { margin-left: 0px; padding-left: 0px; } dl.note { margin-left:-7px; padding-left: 3px; border-left:4px solid; border-color: #D0C000; } dl.warning, dl.attention { margin-left:-7px; padding-left: 3px; border-left:4px solid; border-color: #FF0000; } dl.pre, dl.post, dl.invariant { margin-left:-7px; padding-left: 3px; border-left:4px solid; border-color: #00D000; } dl.deprecated { margin-left:-7px; padding-left: 3px; border-left:4px solid; border-color: #505050; } dl.todo { margin-left:-7px; padding-left: 3px; border-left:4px solid; border-color: #00C0E0; } dl.test { margin-left:-7px; padding-left: 3px; border-left:4px solid; border-color: #3030E0; } dl.bug { margin-left:-7px; padding-left: 3px; border-left:4px solid; border-color: #C08050; } dl.section dd { margin-bottom: 6px; } #projectlogo { text-align: center; vertical-align: bottom; border-collapse: separate; } #projectlogo img { border: 0px none; } #projectname { font: 300% Tahoma, Arial,sans-serif; margin: 0px; padding: 2px 0px; } #projectbrief { font: 120% Tahoma, Arial,sans-serif; margin: 0px; padding: 0px; } #projectnumber { font: 50% Tahoma, Arial,sans-serif; margin: 0px; padding: 0px; } #titlearea { padding: 0px; margin: 0px; width: 100%; border-bottom: 1px solid #5373B4; } .image { text-align: center; } .dotgraph { text-align: center; } .mscgraph { text-align: center; } .diagraph { text-align: center; } .caption { font-weight: bold; } div.zoom { border: 1px solid #90A5CE; } dl.citelist { margin-bottom:50px; } dl.citelist dt { color:#334975; float:left; font-weight:bold; margin-right:10px; padding:5px; } dl.citelist dd { margin:2px 0; padding:5px 0; } div.toc { padding: 14px 25px; background-color: #F4F6FA; border: 1px solid #D8DFEE; border-radius: 7px 7px 7px 7px; float: right; height: auto; margin: 0 20px 10px 10px; width: 200px; } div.toc li { background: url("bdwn.png") no-repeat scroll 0 5px transparent; font: 10px/1.2 Verdana,DejaVu Sans,Geneva,sans-serif; margin-top: 5px; padding-left: 10px; padding-top: 2px; } div.toc h3 { font: bold 12px/1.2 Arial,FreeSans,sans-serif; color: #4665A2; border-bottom: 0 none; margin: 0; } div.toc ul { list-style: none outside none; border: medium none; padding: 0px; } div.toc li.level1 { margin-left: 0px; } div.toc li.level2 { margin-left: 15px; } div.toc li.level3 { margin-left: 30px; } div.toc li.level4 { margin-left: 45px; } .inherit_header { font-weight: bold; color: gray; cursor: pointer; -webkit-touch-callout: none; -webkit-user-select: none; -khtml-user-select: none; -moz-user-select: none; -ms-user-select: none; user-select: none; } .inherit_header td { padding: 6px 0px 2px 5px; } .inherit { display: none; } tr.heading h2 { margin-top: 12px; margin-bottom: 4px; } /* tooltip related style info */ .ttc { position: absolute; display: none; } #powerTip { cursor: default; white-space: nowrap; background-color: white; border: 1px solid gray; border-radius: 4px 4px 4px 4px; box-shadow: 1px 1px 7px gray; display: none; font-size: smaller; max-width: 80%; opacity: 0.9; padding: 1ex 1em 1em; position: absolute; z-index: 2147483647; } #powerTip div.ttdoc { color: grey; font-style: italic; } #powerTip div.ttname a { font-weight: bold; } #powerTip div.ttname { font-weight: bold; } #powerTip div.ttdeci { color: #006318; } #powerTip div { margin: 0px; padding: 0px; font: 12px/16px Roboto,sans-serif; } #powerTip:before, #powerTip:after { content: ""; position: absolute; margin: 0px; } #powerTip.n:after, #powerTip.n:before, #powerTip.s:after, #powerTip.s:before, #powerTip.w:after, #powerTip.w:before, #powerTip.e:after, #powerTip.e:before, #powerTip.ne:after, #powerTip.ne:before, #powerTip.se:after, #powerTip.se:before, #powerTip.nw:after, #powerTip.nw:before, #powerTip.sw:after, #powerTip.sw:before { border: solid transparent; content: " "; height: 0; width: 0; position: absolute; } #powerTip.n:after, #powerTip.s:after, #powerTip.w:after, #powerTip.e:after, #powerTip.nw:after, #powerTip.ne:after, #powerTip.sw:after, #powerTip.se:after { border-color: rgba(255, 255, 255, 0); } #powerTip.n:before, #powerTip.s:before, #powerTip.w:before, #powerTip.e:before, #powerTip.nw:before, #powerTip.ne:before, #powerTip.sw:before, #powerTip.se:before { border-color: rgba(128, 128, 128, 0); } #powerTip.n:after, #powerTip.n:before, #powerTip.ne:after, #powerTip.ne:before, #powerTip.nw:after, #powerTip.nw:before { top: 100%; } #powerTip.n:after, #powerTip.ne:after, #powerTip.nw:after { border-top-color: #ffffff; border-width: 10px; margin: 0px -10px; } #powerTip.n:before { border-top-color: #808080; border-width: 11px; margin: 0px -11px; } #powerTip.n:after, #powerTip.n:before { left: 50%; } #powerTip.nw:after, #powerTip.nw:before { right: 14px; } #powerTip.ne:after, #powerTip.ne:before { left: 14px; } #powerTip.s:after, #powerTip.s:before, #powerTip.se:after, #powerTip.se:before, #powerTip.sw:after, #powerTip.sw:before { bottom: 100%; } #powerTip.s:after, #powerTip.se:after, #powerTip.sw:after { border-bottom-color: #ffffff; border-width: 10px; margin: 0px -10px; } #powerTip.s:before, #powerTip.se:before, #powerTip.sw:before { border-bottom-color: #808080; border-width: 11px; margin: 0px -11px; } #powerTip.s:after, #powerTip.s:before { left: 50%; } #powerTip.sw:after, #powerTip.sw:before { right: 14px; } #powerTip.se:after, #powerTip.se:before { left: 14px; } #powerTip.e:after, #powerTip.e:before { left: 100%; } #powerTip.e:after { border-left-color: #ffffff; border-width: 10px; top: 50%; margin-top: -10px; } #powerTip.e:before { border-left-color: #808080; border-width: 11px; top: 50%; margin-top: -11px; } #powerTip.w:after, #powerTip.w:before { right: 100%; } #powerTip.w:after { border-right-color: #ffffff; border-width: 10px; top: 50%; margin-top: -10px; } #powerTip.w:before { border-right-color: #808080; border-width: 11px; top: 50%; margin-top: -11px; } @media print { #top { display: none; } #side-nav { display: none; } #nav-path { display: none; } body { overflow:visible; } h1, h2, h3, h4, h5, h6 { page-break-after: avoid; } .summary { display: none; } .memitem { page-break-inside: avoid; } #doc-content { margin-left:0 !important; height:auto !important; width:auto !important; overflow:inherit; display:inline; } } pacparser-1.4.5/docs/html/pacparser.html000066400000000000000000000475361464010763600202710ustar00rootroot00000000000000 Pacparser: pacparser
Pacparser
A library to make your web software pac (proxy auto-config) files intelligent.
pacparser

API for pacparser library, a library to use proxy auto-config (PAC) files. See project homepage: http://github.com/pacparser/pacparser for more information. More...

Typedefs

typedef int(* pacparser_error_printer )(const char *fmt, va_list argp)
 Type definition for pacparser_error_printer.
 

Functions

int pacparser_init (void)
 Initializes pac parser. More...
 
int pacparser_parse_pac_file (const char *pacfile)
 Parses the given PAC file. More...
 
int pacparser_parse_pac_string (const char *pacstring)
 Parses the given PAC script string. More...
 
int pacparser_parse_pac (const char *pacfile)
 Parses the gievn pac file. More...
 
char * pacparser_find_proxy (const char *url, const char *host)
 Finds proxy for the given URL and Host. More...
 
char * pacparser_just_find_proxy (const char *pacfile, const char *url, const char *host)
 Finds proxy for the given PAC file, URL and Host. More...
 
void pacparser_cleanup (void)
 Destroys JavaSctipt context. More...
 
void pacparser_setmyip (const char *ip)
 Sets my IP address. More...
 
void pacparser_set_error_printer (pacparser_error_printer func)
 Sets error printing function. More...
 
void pacparser_enable_microsoft_extensions (void)
 (Deprecated) Enable Microsoft IPv6 PAC extensions. More...
 
char * pacparser_version (void)
 Returns pacparser version. More...
 

Detailed Description

API for pacparser library, a library to use proxy auto-config (PAC) files. See project homepage: http://github.com/pacparser/pacparser for more information.

Author
Manu Garg manug.nosp@m.arg@.nosp@m.gmail.nosp@m..com

Function Documentation

int pacparser_init ( void  )

Initializes pac parser.

Returns
0 on failure and 1 on success.

Initializes JavaScript engine and does few basic initializations specific to pacparser.

int pacparser_parse_pac_file ( const char *  pacfile)

Parses the given PAC file.

Parameters
pacfilePAC file to parse.
Returns
0 on failure and 1 on success.

Reads the given PAC file and evaluates it in the JavaScript context created by pacparser_init.

int pacparser_parse_pac_string ( const char *  pacstring)

Parses the given PAC script string.

Parameters
pacstringPAC string to parse.
Returns
0 on failure and 1 on success.

Evaluates the given PAC script string in the JavaScript context created by pacparser_init.

int pacparser_parse_pac ( const char *  pacfile)

Parses the gievn pac file.

Deprecated:
Use pacparser_parse_pac_file instead.
Parameters
pacfilePAC file to parse.
Returns
0 on failure and 1 on success.

Same as pacparser_parse_pac_file. Included only for backward compatibility.

char* pacparser_find_proxy ( const char *  url,
const char *  host 
)

Finds proxy for the given URL and Host.

Parameters
urlURL to find proxy for.
hostHost part of the URL.
Returns
proxy string on success and NULL on error.

Finds proxy for the given URL and Host. This function should be called only after pacparser engine has been initialized (using pacparser_init) and pac script has been parsed (using pacparser_parse_pac_file or pacparser_parse_pac_string).

char* pacparser_just_find_proxy ( const char *  pacfile,
const char *  url,
const char *  host 
)

Finds proxy for the given PAC file, URL and Host.

Parameters
pacfilePAC file to parse.
urlURL to find proxy for.
hostHost part of the URL.
Returns
proxy string on success and NULL on error.

This function is a wrapper around functions pacparser_init, pacparser_parse_pac_file, pacparser_find_proxy and pacparser_cleanup. If you just want to find out proxy for a given set of pac file, url and host, this is the function to call. This function takes care of all the initialization and cleanup.

void pacparser_cleanup ( void  )

Destroys JavaSctipt context.

This function should be called once you're done with using pacparser engine.

void pacparser_setmyip ( const char *  ip)

Sets my IP address.

Parameters
ipCustom IP address.

Sets my IP address to a custom value. This is the IP address returned by myIpAddress() javascript function.

void pacparser_set_error_printer ( pacparser_error_printer  func)

Sets error printing function.

Parameters
funcPrinting function.

Sets error variadic-argument printing function. If not set the messages are printed to stderr. If messages begin with DEBUG: or WARNING:, they are not fatal error messages, otherwise they are. May be called before pacparser_init().

void pacparser_enable_microsoft_extensions ( void  )

(Deprecated) Enable Microsoft IPv6 PAC extensions.

Deprecated. IPv6 extension (*Ex functions) are enabled by default now.

char* pacparser_version ( void  )

Returns pacparser version.

Returns
version string if version defined, "" otherwise.

Version string is determined at the time of build. If built from a released package, version corresponds to the latest release (git) tag. If built from the repository, it corresponds to the head revision of the repo.

pacparser-1.4.5/docs/html/pactester.1.html000066400000000000000000000063231464010763600204270ustar00rootroot00000000000000 "pactester"("1") manual page Table of Contents

Name

pactester - Tool to test proxy auto-config (pac) files.

Synopsis

pactester <-p pacfile> <-u url> [-h host] [-c client_ip] [-e]

pactester <-p pacfile> <-f urlslist> [-c client_ip] [-e]

Description

pactester is a tool to test proxy auto-config (pac) files. It returns the proxy config string for the given URL and the pac file. pactester uses pacparser C library for most of its functionality.

Options

-p pacfile
PAC file to test. Specify "-" to read from the standard input.
-u url
URL to test the PAC file for.
-h host
Host part of the URL. If not specified, it’s determined from the URL.
-c client_ip
Client’s IP address (as returned by the function myIpAddress() in PAC files). If not specified, it defaults to the IP address of the machine on which this tool is running.
-e
Enable Microsoft PAC extensions (dnsResolveEx, myIpAddressEx, isResolvableEx).
-f urlslist
A file containing the list of URLs to be tested. This is good for testing a PAC file against a set of URLs.

Examples

To find out the proxy config string for the pac file "wpad.dat" and the URL "http://www.google.com ":

$ pactester -p wpad.dat -u http://www.google.com

For a client with IP address 10.0.12.123:

$ pactester -p wpad.dat -c 10.0.12.123 -u http://www.google.com

For a pac file hosted at http://wpad/wpad.dat:

$ curl -s http://wpad/wpad.dat | pactester -p - -u http://google.com

Bugs

If you have come across a bug in pactester, please submit a bug report at http://github.com/pacparser/pacparser/issues.

Author

Written by Manu Garg (http://www.manugarg.com).

Resources

Homepage: http://github.com/pacparser/pacparser.


Table of Contents

pacparser-1.4.5/docs/man/000077500000000000000000000000001464010763600152135ustar00rootroot00000000000000pacparser-1.4.5/docs/man/man1/000077500000000000000000000000001464010763600160475ustar00rootroot00000000000000pacparser-1.4.5/docs/man/man1/pactester.1000066400000000000000000000033731464010763600201310ustar00rootroot00000000000000.TH "pactester" "1" "" "" "" .SH "NAME" pactester \- Tool to test proxy auto\-config (pac) files. .SH "SYNOPSIS" .B pactester <\-p pacfile> <\-u url> [\-h host] [\-c client_ip] [\-e] .PP .B pactester <\-p pacfile> <\-f urlslist> [\-c client_ip] [\-e] .SH "DESCRIPTION" pactester is a tool to test proxy auto\-config (pac) files. It returns the proxy config string for the given URL and the pac file. pactester uses pacparser C library for most of its functionality. .SH "OPTIONS" .TP .B \-p pacfile PAC file to test. Specify "-" to read from the standard input. .TP .B \-u url URL to test the PAC file for. .TP .B \-h host Host part of the URL. If not specified, it's determined from the URL. .TP .B \-c client_ip Client's IP address (as returned by the function myIpAddress() in PAC files). If not specified, it defaults to the IP address of the machine on which this tool is running. .TP .B \-e Enable Microsoft PAC extensions (dnsResolveEx, myIpAddressEx, isResolvableEx). .TP .B \-f urlslist A file containing the list of URLs to be tested. This is good for testing a PAC file against a set of URLs. .SH "EXAMPLES" .PP To find out the proxy config string for the pac file "wpad.dat" and the URL "http://www.google.com": .PP $ pactester \-p wpad.dat \-u http://www.google.com For a client with IP address 10.0.12.123: .PP $ pactester \-p wpad.dat \-c 10.0.12.123 \-u http://www.google.com For a pac file hosted at http://wpad/wpad.dat: .PP $ curl \-s http://wpad/wpad.dat | pactester \-p \- \-u http://google.com .SH "BUGS" If you have come across a bug in pactester, please submit a bug report at http://github.com/pacparser/pacparser/issues. .SH "AUTHOR" Written by Manu Garg (http://www.manugarg.com). .SH "RESOURCES" Homepage: http://github.com/pacparser/pacparser. pacparser-1.4.5/docs/man/man3/000077500000000000000000000000001464010763600160515ustar00rootroot00000000000000pacparser-1.4.5/docs/man/man3/pacparser.3000066400000000000000000000140171464010763600201200ustar00rootroot00000000000000.TH "pacparser" 3 "Tue Sep 1 2015" "Pacparser" \" -*- nroff -*- .ad l .nh .SH NAME pacparser - Library to parse proxy auto-confg (PAC) files. .PP API for pacparser library, a library to use proxy auto-config (PAC) files\&. See project homepage: http://github.com/pacparser/pacparser for more information\&. .SS "Typedefs" .in +1c .ti -1c .RI "typedef int(* \fBpacparser_error_printer\fP )(const char *fmt, va_list argp)" .br .RI "\fIType definition for pacparser_error_printer\&. \fP" .in -1c .SS "Functions" .in +1c .ti -1c .RI "int \fBpacparser_init\fP (void)" .br .RI "\fIInitializes pac parser\&. \fP" .ti -1c .RI "int \fBpacparser_parse_pac_file\fP (const char *pacfile)" .br .RI "\fIParses the given PAC file\&. \fP" .ti -1c .RI "int \fBpacparser_parse_pac_string\fP (const char *pacstring)" .br .RI "\fIParses the given PAC script string\&. \fP" .ti -1c .RI "int \fBpacparser_parse_pac\fP (const char *pacfile)" .br .RI "\fIParses the gievn pac file\&. \fP" .ti -1c .RI "char * \fBpacparser_find_proxy\fP (const char *url, const char *host)" .br .RI "\fIFinds proxy for the given URL and Host\&. \fP" .ti -1c .RI "char * \fBpacparser_just_find_proxy\fP (const char *pacfile, const char *url, const char *host)" .br .RI "\fIFinds proxy for the given PAC file, URL and Host\&. \fP" .ti -1c .RI "void \fBpacparser_cleanup\fP (void)" .br .RI "\fIDestroys JavaSctipt context\&. \fP" .ti -1c .RI "void \fBpacparser_setmyip\fP (const char *ip)" .br .RI "\fISets my IP address\&. \fP" .ti -1c .RI "void \fBpacparser_set_error_printer\fP (\fBpacparser_error_printer\fP func)" .br .RI "\fISets error printing function\&. \fP" .ti -1c .RI "void \fBpacparser_enable_microsoft_extensions\fP (void)" .br .RI "\fI(Deprecated) Enable Microsoft IPv6 PAC extensions\&. \fP" .ti -1c .RI "char * \fBpacparser_version\fP (void)" .br .RI "\fIReturns pacparser version\&. \fP" .in -1c .SH "Detailed Description" .PP API for pacparser library, a library to use proxy auto-config (PAC) files\&. See project homepage: http://github.com/pacparser/pacparser for more information\&. .PP \fBAuthor:\fP .RS 4 Manu Garg manugarg@gmail.com .RE .PP .SH "Function Documentation" .PP .SS "int pacparser_init (void)" .PP Initializes pac parser\&. .PP \fBReturns:\fP .RS 4 0 on failure and 1 on success\&. .RE .PP Initializes JavaScript engine and does few basic initializations specific to pacparser\&. .SS "int pacparser_parse_pac_file (const char *pacfile)" .PP Parses the given PAC file\&. .PP \fBParameters:\fP .RS 4 \fIpacfile\fP PAC file to parse\&. .RE .PP \fBReturns:\fP .RS 4 0 on failure and 1 on success\&. .RE .PP Reads the given PAC file and evaluates it in the JavaScript context created by pacparser_init\&. .SS "int pacparser_parse_pac_string (const char *pacstring)" .PP Parses the given PAC script string\&. .PP \fBParameters:\fP .RS 4 \fIpacstring\fP PAC string to parse\&. .RE .PP \fBReturns:\fP .RS 4 0 on failure and 1 on success\&. .RE .PP Evaluates the given PAC script string in the JavaScript context created by pacparser_init\&. .SS "int pacparser_parse_pac (const char *pacfile)" .PP Parses the gievn pac file\&. .PP \fBDeprecated\fP .RS 4 Use pacparser_parse_pac_file instead\&. .PP \fBParameters:\fP .RS 4 \fIpacfile\fP PAC file to parse\&. .RE .PP \fBReturns:\fP .RS 4 0 on failure and 1 on success\&. .RE .PP .RE .PP .PP Same as pacparser_parse_pac_file\&. Included only for backward compatibility\&. .SS "char* pacparser_find_proxy (const char *url, const char *host)" .PP Finds proxy for the given URL and Host\&. .PP \fBParameters:\fP .RS 4 \fIurl\fP URL to find proxy for\&. .br \fIhost\fP Host part of the URL\&. .RE .PP \fBReturns:\fP .RS 4 proxy string on success and NULL on error\&. .RE .PP Finds proxy for the given URL and Host\&. This function should be called only after pacparser engine has been initialized (using pacparser_init) and pac script has been parsed (using pacparser_parse_pac_file or pacparser_parse_pac_string)\&. .SS "char* pacparser_just_find_proxy (const char *pacfile, const char *url, const char *host)" .PP Finds proxy for the given PAC file, URL and Host\&. .PP \fBParameters:\fP .RS 4 \fIpacfile\fP PAC file to parse\&. .br \fIurl\fP URL to find proxy for\&. .br \fIhost\fP Host part of the URL\&. .RE .PP \fBReturns:\fP .RS 4 proxy string on success and NULL on error\&. .RE .PP This function is a wrapper around functions pacparser_init, pacparser_parse_pac_file, pacparser_find_proxy and pacparser_cleanup\&. If you just want to find out proxy for a given set of pac file, url and host, this is the function to call\&. This function takes care of all the initialization and cleanup\&. .SS "void pacparser_cleanup (void)" .PP Destroys JavaSctipt context\&. This function should be called once you're done with using pacparser engine\&. .SS "void pacparser_setmyip (const char *ip)" .PP Sets my IP address\&. .PP \fBParameters:\fP .RS 4 \fIip\fP Custom IP address\&. .RE .PP Sets my IP address to a custom value\&. This is the IP address returned by myIpAddress() javascript function\&. .SS "void pacparser_set_error_printer (\fBpacparser_error_printer\fPfunc)" .PP Sets error printing function\&. .PP \fBParameters:\fP .RS 4 \fIfunc\fP Printing function\&. .RE .PP Sets error variadic-argument printing function\&. If not set the messages are printed to stderr\&. If messages begin with DEBUG: or WARNING:, they are not fatal error messages, otherwise they are\&. May be called before \fBpacparser_init()\fP\&. .SS "void pacparser_enable_microsoft_extensions (void)" .PP (Deprecated) Enable Microsoft IPv6 PAC extensions\&. Deprecated\&. IPv6 extension (*Ex functions) are enabled by default now\&. .SS "char* pacparser_version (void)" .PP Returns pacparser version\&. .PP \fBReturns:\fP .RS 4 version string if version defined, '' otherwise\&. .RE .PP Version string is determined at the time of build\&. If built from a released package, version corresponds to the latest release (git) tag\&. If built from the repository, it corresponds to the head revision of the repo\&. .SH "Author" .PP Generated automatically by Doxygen for Pacparser from the source code\&. pacparser-1.4.5/docs/man/man3/pacparser_cleanup.3000066400000000000000000000000251464010763600216210ustar00rootroot00000000000000.so man3/pacparser.3 pacparser-1.4.5/docs/man/man3/pacparser_enable_microsoft_extensions.3000066400000000000000000000000251464010763600257640ustar00rootroot00000000000000.so man3/pacparser.3 pacparser-1.4.5/docs/man/man3/pacparser_error_printer.3000066400000000000000000000000251464010763600230660ustar00rootroot00000000000000.so man3/pacparser.3 pacparser-1.4.5/docs/man/man3/pacparser_find_proxy.3000066400000000000000000000000251464010763600223530ustar00rootroot00000000000000.so man3/pacparser.3 pacparser-1.4.5/docs/man/man3/pacparser_init.3000066400000000000000000000000251464010763600211350ustar00rootroot00000000000000.so man3/pacparser.3 pacparser-1.4.5/docs/man/man3/pacparser_just_find_proxy.3000066400000000000000000000000251464010763600234200ustar00rootroot00000000000000.so man3/pacparser.3 pacparser-1.4.5/docs/man/man3/pacparser_parse_pac.3000066400000000000000000000000251464010763600221270ustar00rootroot00000000000000.so man3/pacparser.3 pacparser-1.4.5/docs/man/man3/pacparser_parse_pac_file.3000066400000000000000000000000251464010763600231260ustar00rootroot00000000000000.so man3/pacparser.3 pacparser-1.4.5/docs/man/man3/pacparser_parse_pac_string.3000066400000000000000000000000251464010763600235150ustar00rootroot00000000000000.so man3/pacparser.3 pacparser-1.4.5/docs/man/man3/pacparser_set_error_printer.3000066400000000000000000000000251464010763600237410ustar00rootroot00000000000000.so man3/pacparser.3 pacparser-1.4.5/docs/man/man3/pacparser_setmyip.3000066400000000000000000000000251464010763600216640ustar00rootroot00000000000000.so man3/pacparser.3 pacparser-1.4.5/docs/man/man3/pacparser_version.3000066400000000000000000000000251464010763600216570ustar00rootroot00000000000000.so man3/pacparser.3 pacparser-1.4.5/examples/000077500000000000000000000000001464010763600153265ustar00rootroot00000000000000pacparser-1.4.5/examples/fetchurl.py000077500000000000000000000062641464010763600175270ustar00rootroot00000000000000#!/usr/bin/python # Copyright (C) 2008 Manu Garg. # Author: Manu Garg # # pacparser is a library that provides methods to parse proxy auto-config # (PAC) files. Please read README file included with this package for more # information about this library. # # pacparser is free software; you can redistribute it and/or # modify it under the terms of the GNU Lesser General Public # License as published by the Free Software Foundation; either # version 2.1 of the License, or (at your option) any later version. # pacparser is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU # Lesser General Public License for more details. # You should have received a copy of the GNU Lesser General Public # License along with this library; if not, write to the Free Software # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, # USA """ This script demonstrates how python web clients can use proxy auto-config (PAC) files for proxy configuration using pacparser. It take a PAC file and an url as arguments, fetches the URL using the proxy as determined by PAC file and URL and returns the retrieved webpage. """ __author__ = 'manugarg@gmail.com (Manu Garg)' __copyright__ = 'Copyright (C) 2008 Manu Garg' __license__ = 'LGPL' import pacparser import socket import sys import urllib def fetch_url_using_pac(pac, url): try: proxy_string = pacparser.just_find_proxy(pac, url) except: sys.stderr.write('could not determine proxy using Pacfile\n') return None proxylist = proxy_string.split(";") proxies = None # Dictionary to be passed to urlopen method of urllib while proxylist: proxy = proxylist.pop(0).strip() if 'DIRECT' in proxy: proxies = {} break if proxy[0:5].upper() == 'PROXY': proxy = proxy[6:].strip() if isproxyalive(proxy): proxies = {'http': 'http://%s' % proxy} break try: sys.stderr.write('trying to fetch the page using proxy %s\n' % proxy) response = urllib.urlopen(url, proxies=proxies) except Exception, e: sys.stderr.write('could not fetch webpage %s using proxy %s\n' % (url, proxies)) sys.stderr.write(str(e)+'\n') return None return response def isproxyalive(proxy): host_port = proxy.split(":") if len(host_port) != 2: sys.stderr.write('proxy host is not defined as host:port\n') return False s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) s.settimeout(10) try: s.connect((host_port[0], int(host_port[1]))) except Exception, e: sys.stderr.write('proxy %s is not accessible\n' % proxy) sys.stderr.write(str(e)+'\n') return False s.close() return True def main(): if len(sys.argv) != 3: print('Not enough arguments') print('Usage:\n%s ' % sys.argv[0]) return None pacfile = sys.argv[1] url = sys.argv[2] response = fetch_url_using_pac(pacfile, url) if response: print(response.read()) else: sys.stderr.write('URL %s could not be retrieved using PAC file %s.' % (url, pacfile)) if __name__ == '__main__': main() pacparser-1.4.5/examples/pactest.c000066400000000000000000000005731464010763600171420ustar00rootroot00000000000000#include int pacparser_init(); int pacparser_parse_pac(char* pacfile); char *pacparser_find_proxy(char *url, char *host); void pacparser_cleanup(); int main(int argc, char* argv[]) { char *proxy = NULL; pacparser_init(); pacparser_parse_pac(argv[1]); proxy = pacparser_find_proxy(argv[2], argv[3]); if(proxy) printf("%s\n", proxy); pacparser_cleanup(); } pacparser-1.4.5/examples/pactest.py000077500000000000000000000004061464010763600173460ustar00rootroot00000000000000#!/usr/bin/python import pacparser pacparser.init() pacparser.parse_pac("wpad.dat") proxy = pacparser.find_proxy("http://www.manugarg.com") print(proxy) pacparser.cleanup() # Or simply, print(pacparser.just_find_proxy("wpad.dat", "http://www2.manugarg.com")) pacparser-1.4.5/examples/wpad.dat000066400000000000000000000006561464010763600167620ustar00rootroot00000000000000// Go direct for plain hostnames and any host in .manugarg.com domain except // for www and www.manugarg.com. // Go via proxy for all other hosts. function FindProxyForURL(url, host) { if ((isPlainHostName(host) || dnsDomainIs(host, ".manugarg.com")) && !localHostOrDomainIs(host, "www.manugarg.com")) return "DIRECT"; else return "PROXY proxy1.manugarg.com:3128; PROXY proxy2.manugarg.com:3128; DIRECT"; } pacparser-1.4.5/sonar-project.properties000066400000000000000000000003421464010763600204130ustar00rootroot00000000000000sonar.projectKey=manugarg_pacparser sonar.organization=manugarg sonar.projectName=pacparser sonar.sources=src sonar.cfamily.compile-commands=compile_commands.json sonar.sourceEncoding=UTF-8 sonar.host.url=https://sonarcloud.iopacparser-1.4.5/src/000077500000000000000000000000001464010763600142775ustar00rootroot00000000000000pacparser-1.4.5/src/Makefile000066400000000000000000000127741464010763600157520ustar00rootroot00000000000000# Copyright (C) 2007 Manu Garg. # Author: Manu Garg # # Makefile for pacparser. Please read README file included with this package # for more information about pacparser. # # pacparser is free software; you can redistribute it and/or # modify it under the terms of the GNU Lesser General Public # License as published by the Free Software Foundation; either # version 3 of the License, or (at your option) any later version. # pacparser is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU # Lesser General Public License for more details. # You should have received a copy of the GNU Lesser General Public # License along with this library; if not, write to the Free Software # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA # This file is not part of the source code repository. It's generated by the # packaging script. -include version.mk ifeq ($(shell uname),FreeBSD) SHELL := /usr/local/bin/bash else SHELL := /bin/bash endif VERSION ?= $(shell git describe --always --tags --candidate=100) GIT_TAG := $(shell git describe --exact-match --exclude tip --tags HEAD 2>/dev/null || /bin/true) OS_ARCH := $(subst /,_,$(shell uname -s | sed /\ /s//_/)) LIBRARY_NAME = libpacparser LIB_VER = 1 # This Makefile should at least work on Linux and Mac OS X. It should work on # most other types of Unix systems too, but I have not put any conscious effort # for that. # # Set variables according to Linux. SO_SUFFIX = so LIBRARY = $(LIBRARY_NAME).$(SO_SUFFIX).$(LIB_VER) MKSHLIB = $(CC) -shared LIB_OPTS = -Wl,-soname=$(LIBRARY) -Wl,-exclude-libs=libjs.a SHFLAGS = -fPIC SMCFLAGS = -DHAVE_VA_COPY -DVA_COPY=va_copy ifeq ($(OS_ARCH),FreeBSD) PREFIX ?= /usr/local endif ifeq ($(OS_ARCH),Darwin) PREFIX ?= /usr/local MAC_MAJOR_VERSION := $(shell sw_vers -productVersion | cut -d. -f1) MAC_GT_OS11 := $(shell [ $(MAC_MAJOR_VERSION) -le 10 ] && echo false) SO_SUFFIX = dylib LIBRARY = $(LIBRARY_NAME).$(LIB_VER).$(SO_SUFFIX) MKSHLIB = $(CC) -dynamiclib -framework System LIB_OPTS = -install_name $(PREFIX)/lib/$(notdir $@) SHFLAGS = ifeq ($(MAC_GT_OS11),false) MAC_MINOR_VERSION := $(shell sw_vers -productVersion | cut -d. -f2) MAC_GT_10_5 := $(shell [ $(MAC_MINOR_VERSION) -le 5 ] && echo false) ifeq ($(MAC_GT_10_5),false) SMCFLAGS = endif endif endif PREFIX ?= /usr MAINT_CFLAGS := -g -DXP_UNIX -Wall -DVERSION=$(VERSION) ifndef PYTHON PYTHON = python endif # Spidermonkey library. MAINT_CFLAGS += -Ispidermonkey/js/src LIBRARY_LINK = $(LIBRARY_NAME).$(SO_SUFFIX) PREFIX := $(DESTDIR)$(PREFIX) LIB_PREFIX = $(PREFIX)/lib INC_PREFIX = $(PREFIX)/include BIN_PREFIX = $(PREFIX)/bin DOC_PREFIX = $(PREFIX)/share/doc/pacparser MAN_PREFIX = $(PREFIX)/share/man .PHONY: clean pymod install-pymod all: testpactester jsapi_buildstamp: spidermonkey/js/src cd spidermonkey && SMCFLAGS="$(SHFLAGS) $(SMCFLAGS)" $(MAKE) jsapi touch jsapi_buildstamp spidermonkey/libjs.a: spidermonkey/js/src cd spidermonkey && SMCFLAGS="$(SHFLAGS) $(SMCFLAGS)" $(MAKE) jslib pacparser.o: pacparser.c pac_utils.h pacparser.h jsapi_buildstamp $(CC) $(MAINT_CFLAGS) $(CFLAGS) $(SHFLAGS) -c pacparser.c -o pacparser.o touch pymod/pacparser_o_buildstamp $(LIBRARY): pacparser.o spidermonkey/libjs.a $(MKSHLIB) $(MAINT_CFLAGS) $(CFLAGS) $(LDFLAGS) $(LIB_OPTS) -o $(LIBRARY) pacparser.o spidermonkey/libjs.a -lm $(LIBRARY_LINK): $(LIBRARY) ln -sf $(LIBRARY) $(LIBRARY_LINK) pactester: pactester.c pacparser.h pacparser.o spidermonkey/libjs.a $(CC) $(MAINT_CFLAGS) $(CFLAGS) $(LDFLAGS) pactester.c pacparser.o spidermonkey/libjs.a -o pactester -lm -L. -I. testpactester: pactester $(LIBRARY_LINK) echo "Running tests for pactester." NO_INTERNET=$(NO_INTERNET) ../tests/runtests.sh docs: ../tools/generatedocs.sh install: all install -d $(LIB_PREFIX) $(INC_PREFIX) $(BIN_PREFIX) install -m 644 $(LIBRARY) $(LIB_PREFIX)/$(LIBRARY) ln -sf $(LIBRARY) $(LIB_PREFIX)/$(LIBRARY_LINK) install -m 755 pactester $(BIN_PREFIX)/pactester install -m 644 pacparser.h $(INC_PREFIX)/pacparser.h # install pactester manpages install -d $(MAN_PREFIX)/man1/ (test -d ../docs && install -m 644 ../docs/man/man1/*.1 $(MAN_PREFIX)/man1/) || true # install pacparser manpages install -d $(MAN_PREFIX)/man3/ (test -d ../docs && install -m 644 ../docs/man/man3/*.3 $(MAN_PREFIX)/man3/) || true # install html docs install -d $(DOC_PREFIX)/html/ (test -d ../docs/html && install -m 644 ../docs/html/* $(DOC_PREFIX)/html/) || true # install examples install -d $(DOC_PREFIX)/examples/ (test -d ../examples && install -m 644 ../examples/* $(DOC_PREFIX)/examples/) || true dist: all bindir=pacparser-$(VERSION)-$(DIST_OS_SUFFIX); \ bindir=$${bindir/-latest/}; \ PREFIX= $(MAKE) install DESTDIR=$${bindir}; \ zip -r $${bindir}.zip $${bindir}/. # Targets to build python module pymod: pacparser.o pacparser.h spidermonkey/libjs.a cd pymod && ARCHFLAGS="" $(PYTHON) setup.py build $(PYTHON) ../tests/runtests.py pymod-dist: pacparser.o pacparser.h spidermonkey/libjs.a cd pymod && ARCHFLAGS="" $(PYTHON) setup.py build cd pymod && ARCHFLAGS="" $(PYTHON) setup.py dist $(PYTHON) ../tests/runtests.py install-pymod: pymod cd pymod && ARCHFLAGS="" $(PYTHON) setup.py install --root="$(DESTDIR)/" $(EXTRA_ARGS) clean: rm -f $(LIBRARY_LINK) $(LIBRARY) pacparser.o pactester pymod/pacparser_o_buildstamp jsapi_buildstamp rm -rf dist cd pymod && $(PYTHON) setup.py clean --all cd spidermonkey && $(MAKE) clean pacparser-1.4.5/src/Makefile.win32000066400000000000000000000062441464010763600167060ustar00rootroot00000000000000# Copyright (C) 2007-2022 Manu Garg. # Author: Manu Garg # # Makefile for pacparser. Please read README file included with this package # for more information about pacparser. # # pacparser is free software; you can redistribute it and/or # modify it under the terms of the GNU Lesser General Public # License as published by the Free Software Foundation; either # version 3 of the License, or (at your option) any later version. # pacparser is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU # Lesser General Public License for more details. # You should have received a copy of the GNU Lesser General Public # License along with this library; if not, write to the Free Software # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA # This file is not part of the source code repository. It's generated by the # packaging script. -include version.mk ifeq ($(OS),Windows_NT) RM = del /Q /F CP = copy /Y ifdef ComSpec SHELL := $(ComSpec) endif ifdef COMSPEC SHELL := $(COMSPEC) endif else RM = rm -rf CP = cp -f endif VERSION ?= $(shell git describe --always --tags --candidate=100) LIB_VER=1 CFLAGS=-g -DXP_WIN -DWINVER=0x0501 -DVERSION=$(VERSION) -Ispidermonkey/js/src -Wall CC=gcc PYTHON ?= python .PHONY: clean pymod install-pymod all: pacparser.dll pactester pacparser.o: pacparser.c pac_utils.h js.lib $(CC) $(CFLAGS) -c pacparser.c -o pacparser.o js.lib: $(MAKE) -C spidermonkey -f Makefile.win32 pacparser.dll: pacparser.o spidermonkey/js.lib $(CC) -shared -o pacparser.dll \ -Wl,--output-def,pacparser.def \ -Wl,--out-implib,libpacparser.a \ -Wl,--export-all-symbols \ pacparser.o -ljs -Lspidermonkey -lws2_32 pacparser.lib: pacparser.dll pacparser.def lib /machine:i386 /def:pacparser.def pactester: pactester.c pacparser.h pacparser.o $(CC) pactester.c pacparser.o -o pactester -ljs -Lspidermonkey -lws2_32 dist: pacparser.dll pactester pacparser.def if exist dist rmdir /s /q dist mkdir dist $(CP) pacparser.dll dist $(CP) pacparser.h dist $(CP) pactester.exe dist $(CP) pacparser.def dist if exist pacparser.lib $(CP) pacparser.lib dist $(CP) ..\README.md dist\README.txt $(CP) ..\COPYING dist\COPYING.txt $(CP) ..\INSTALL dist\INSTALL.txt mkdir dist\docs $(CP) ..\README.win32.md dist\docs if exist ..\docs\html xcopy ..\docs\html dist\docs # Targets to build python module pymod: pacparser.h pacparser.dll pacparser.o js.lib cd pymod && $(PYTHON) setup.py build --compiler=mingw32 cd .. && $(PYTHON) tests/runtests.py pymod-dist: pacparser.h pacparser.dll pacparser.o js.lib cd pymod && $(PYTHON) setup.py build --compiler=mingw32 && $(PYTHON) setup.py dist cd .. && $(PYTHON) tests/runtests.py pymod-%: pacparser.h pacparser.dll pacparser.o js.lib cd pymod && py -$* setup.py build --compiler=mingw32 cd .. && py -$* tests\runtests.py pymod-dist-%: cd pymod && py -$* setup.py dist clean: $(RM) pacparser.dll *.lib pacparser.def pacparser.exp pacparser.o pactester.exe libpacparser.a $(MAKE) -C spidermonkey -f Makefile.win32 clean cd pymod && $(PYTHON) setup.py clean --all $(RM) dist pacparser-1.4.5/src/pac_utils.h000066400000000000000000000254641464010763600164460ustar00rootroot00000000000000// Copyright (C) 2007-2023 Manu Garg. // Author: Manu Garg // // pac_utils.h defines some of the functions used by PAC files. This file is // packaged with pacparser source code and is required for compiling pacparser. // Please read README file included with this package for more information // about pacparser. // Note: This file is derived from "nsProxyAutoConfig.js" file that comes with // mozilla source code. Please check out the following for initial developer // and contributors: //http://lxr.mozilla.org/seamonkey/source/netwerk/base/src/nsProxyAutoConfig.js // // This file is free software; you can redistribute it and/or // modify it under the terms of the GNU Lesser General Public // License as published by the Free Software Foundation; either // version 3 of the License, or (at your option) any later version. // pacparser is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU // Lesser General Public License for more details. // You should have received a copy of the GNU Lesser General Public // License along with this library; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, // USA static const char *pacUtils = "function dnsDomainIs(host, domain) {\n" " return (host.length >= domain.length &&\n" " host.substring(host.length - domain.length) == domain);\n" "}\n" "function dnsDomainLevels(host) {\n" " return host.split('.').length-1;\n" "}\n" "function convert_addr(ipchars) {\n" " var bytes = ipchars.split('.');\n" " var result = ((bytes[0] & 0xff) << 24) |\n" " ((bytes[1] & 0xff) << 16) |\n" " ((bytes[2] & 0xff) << 8) |\n" " (bytes[3] & 0xff);\n" " return result;\n" "}\n" "function isInNet(ipaddr, pattern, maskstr) {\n" " var test = /^(\\d{1,3})\\.(\\d{1,3})\\.(\\d{1,3})\\.(\\d{1,3})$/(ipaddr);\n" " if (test == null) {\n" " ipaddr = dnsResolve(ipaddr);\n" " if (ipaddr == null)\n" " return false;\n" " } else if (test[1] > 255 || test[2] > 255 || \n" " test[3] > 255 || test[4] > 255) {\n" " return false; // not an IP address\n" " }\n" " var host = convert_addr(ipaddr);\n" " var pat = convert_addr(pattern);\n" " var mask = convert_addr(maskstr);\n" " return ((host & mask) == (pat & mask));\n" " \n" "}\n" "function convert_addr6(ipchars) {\n" " ipchars = ipchars.replace(/(^:|:$)/, '');\n" " var fields = ipchars.split(':');\n" " var diff = 8 - fields.length;\n" " for (var i = 0; i < fields.length; i++) {\n" " if (fields[i] == '') {\n" " fields[i] = '0';\n" " // inject 'diff' number of '0' elements here.\n" " for (var j = 0; j < diff; j++) {\n" " fields.splice(i++, 0, '0');\n" " }\n" " break;\n" " }\n" " }\n" " var result = [];\n" " for (var i = 0; i < fields.length; i++) {\n" " result.push(parseInt(fields[i], 16));\n" " }\n" " return result;\n" "}\n" "function isInNetEx6(ipaddr, prefix, prefix_len) {\n" " if (prefix_len > 128) {\n" " return false;\n" " }\n" " prefix = convert_addr6(prefix);\n" " ip = convert_addr6(ipaddr);\n" " // Prefix match strategy:\n" " // Compare only prefix length bits between 'ipaddr' and 'prefix'\n" " // Match in the batches of 16-bit fields \n" " prefix_rem = prefix_len % 16;\n" " prefix_nfields = (prefix_len - prefix_rem) / 16;\n" "\n" " for (var i = 0; i < prefix_nfields; i++) {\n" " if (ip[i] != prefix[i]) {\n" " return false;\n" " }\n" " }\n" " if (prefix_rem > 0) {\n" " // Compare remaining bits\n" " prefix_bits = prefix[prefix_nfields] >> (16 - prefix_rem);\n" " ip_bits = ip[prefix_nfields] >> (16 - prefix_rem);\n" " if (ip_bits != prefix_bits) {\n" " return false;\n" " }\n" " }\n" " return true;\n" "}\n" "function isInNetEx4(ipaddr, prefix, prefix_len) {\n" " if (prefix_len > 32) {\n" " return false;\n" " }\n" " var netmask = [];\n" " for (var i = 1; i < 5; i++) {\n" " var shift_len = 8 * i - prefix_len;\n" " if (shift_len <= 0) {\n" " netmask.push(255)\n" " } else {\n" " netmask.push((0xff >> shift_len) << shift_len);\n" " }\n" " }\n" " return isInNet(ipaddr, prefix, netmask.join('.'));\n" "}\n" "function isInNetEx(ipaddr, prefix) {\n" " prefix_a = prefix.split('/');\n" " if (prefix_a.length != 2) {\n" " return false;\n" " }\n" " var test = /^\\d{1,3}\\.\\d{1,3}\\.\\d{1,3}\\.\\d{1,3}$/.test(ipaddr);\n" " if (!test) {\n" " return isInNetEx6(ipaddr, prefix_a[0], prefix_a[1]);\n" " } else {\n" " return isInNetEx4(ipaddr, prefix_a[0], prefix_a[1]);\n" " }\n" "}\n" "function isPlainHostName(host) {\n" " return (host.search('\\\\.') == -1);\n" "}\n" "function isResolvable(host) {\n" " var ip = dnsResolve(host);\n" " return (ip != null);\n" "}\n" "if (typeof(dnsResolveEx) == \"function\") {\n" "function isResolvableEx(host) {\n" " var ip = dnsResolveEx(host);\n" " return (ip != null);\n" "}\n" "}\n" "function localHostOrDomainIs(host, hostdom) {\n" " return (host == hostdom) ||\n" " (hostdom.lastIndexOf(host + '.', 0) == 0);\n" "}\n" "function shExpMatch(url, pattern) {\n" " pattern = pattern.replace(/\\./g, '\\\\.');\n" " pattern = pattern.replace(/\\*/g, '.*');\n" " pattern = pattern.replace(/\\?/g, '.');\n" " var newRe = new RegExp('^'+pattern+'$');\n" " return newRe.test(url);\n" "}\n" "var wdays = {SUN: 0, MON: 1, TUE: 2, WED: 3, THU: 4, FRI: 5, SAT: 6};\n" "var months = {JAN: 0, FEB: 1, MAR: 2, APR: 3, MAY: 4, JUN: 5, JUL: 6, AUG: 7, SEP: 8, OCT: 9, NOV: 10, DEC: 11};\n" "function weekdayRange() {\n" " function getDay(weekday) {\n" " if (weekday in wdays) {\n" " return wdays[weekday];\n" " }\n" " return -1;\n" " }\n" " var date = new Date();\n" " var argc = arguments.length;\n" " var wday;\n" " if (argc < 1)\n" " return false;\n" " if (arguments[argc - 1] == 'GMT') {\n" " argc--;\n" " wday = date.getUTCDay();\n" " } else {\n" " wday = date.getDay();\n" " }\n" " var wd1 = getDay(arguments[0]);\n" " var wd2 = (argc == 2) ? getDay(arguments[1]) : wd1;\n" " return (wd1 == -1 || wd2 == -1) ? false\n" " : (wd1 <= wday && wday <= wd2);\n" "}\n" "function dateRange() {\n" " function getMonth(name) {\n" " if (name in months) {\n" " return months[name];\n" " }\n" " return -1;\n" " }\n" " var date = new Date();\n" " var argc = arguments.length;\n" " if (argc < 1) {\n" " return false;\n" " }\n" " var isGMT = (arguments[argc - 1] == 'GMT');\n" "\n" " if (isGMT) {\n" " argc--;\n" " }\n" " // function will work even without explicit handling of this case\n" " if (argc == 1) {\n" " var tmp = parseInt(arguments[0]);\n" " if (isNaN(tmp)) {\n" " return ((isGMT ? date.getUTCMonth() : date.getMonth()) ==\n" "getMonth(arguments[0]));\n" " } else if (tmp < 32) {\n" " return ((isGMT ? date.getUTCDate() : date.getDate()) == tmp);\n" " } else { \n" " return ((isGMT ? date.getUTCFullYear() : date.getFullYear()) ==\n" "tmp);\n" " }\n" " }\n" " var year = date.getFullYear();\n" " var date1, date2;\n" " date1 = new Date(year, 0, 1, 0, 0, 0);\n" " date2 = new Date(year, 11, 31, 23, 59, 59);\n" " var adjustMonth = false;\n" " for (var i = 0; i < (argc >> 1); i++) {\n" " var tmp = parseInt(arguments[i]);\n" " if (isNaN(tmp)) {\n" " var mon = getMonth(arguments[i]);\n" " date1.setMonth(mon);\n" " } else if (tmp < 32) {\n" " adjustMonth = (argc <= 2);\n" " date1.setDate(tmp);\n" " } else {\n" " date1.setFullYear(tmp);\n" " }\n" " }\n" " for (var i = (argc >> 1); i < argc; i++) {\n" " var tmp = parseInt(arguments[i]);\n" " if (isNaN(tmp)) {\n" " var mon = getMonth(arguments[i]);\n" " date2.setMonth(mon);\n" " } else if (tmp < 32) {\n" " date2.setDate(tmp);\n" " } else {\n" " date2.setFullYear(tmp);\n" " }\n" " }\n" " if (adjustMonth) {\n" " date1.setMonth(date.getMonth());\n" " date2.setMonth(date.getMonth());\n" " }\n" " if (isGMT) {\n" " var tmp = date;\n" " tmp.setFullYear(date.getUTCFullYear());\n" " tmp.setMonth(date.getUTCMonth());\n" " tmp.setDate(date.getUTCDate());\n" " tmp.setHours(date.getUTCHours());\n" " tmp.setMinutes(date.getUTCMinutes());\n" " tmp.setSeconds(date.getUTCSeconds());\n" " date = tmp;\n" " }\n" " return ((date1 <= date) && (date <= date2));\n" "}\n" "function timeRange() {\n" " var argc = arguments.length;\n" " var date = new Date();\n" " var isGMT= false;\n" "\n" " if (argc < 1) {\n" " return false;\n" " }\n" " if (arguments[argc - 1] == 'GMT') {\n" " isGMT = true;\n" " argc--;\n" " }\n" "\n" " var hour = isGMT ? date.getUTCHours() : date.getHours();\n" " var date1, date2;\n" " date1 = new Date();\n" " date2 = new Date();\n" "\n" " if (argc == 1) {\n" " return (hour == arguments[0]);\n" " } else if (argc == 2) {\n" " return ((arguments[0] <= hour) && (hour <= arguments[1]));\n" " } else {\n" " switch (argc) {\n" " case 6:\n" " date1.setSeconds(arguments[2]);\n" " date2.setSeconds(arguments[5]);\n" " case 4:\n" " var middle = argc >> 1;\n" " date1.setHours(arguments[0]);\n" " date1.setMinutes(arguments[1]);\n" " date2.setHours(arguments[middle]);\n" " date2.setMinutes(arguments[middle + 1]);\n" " if (middle == 2) {\n" " date2.setSeconds(59);\n" " }\n" " break;\n" " default:\n" " throw 'timeRange: bad number of arguments'\n" " }\n" " }\n" "\n" " if (isGMT) {\n" " date.setFullYear(date.getUTCFullYear());\n" " date.setMonth(date.getUTCMonth());\n" " date.setDate(date.getUTCDate());\n" " date.setHours(date.getUTCHours());\n" " date.setMinutes(date.getUTCMinutes());\n" " date.setSeconds(date.getUTCSeconds());\n" " }\n" " return ((date1 <= date) && (date <= date2));\n" "}\n" "function findProxyForURL(url, host) {\n" " if (typeof FindProxyForURLEx == 'function') {\n" " return FindProxyForURLEx(url, host);\n" " } else {\n" " return FindProxyForURL(url, host);\n" " }\n" "}\n"; pacparser-1.4.5/src/pacparser.c000066400000000000000000000402151464010763600164250ustar00rootroot00000000000000// Copyright (C) 2007-2023 Manu Garg. // Author: Manu Garg // // pacparser is a library that provides methods to parse proxy auto-config // (PAC) files. Please read README file included with this package for more // information about this library. // // pacparser is free software; you can redistribute it and/or // modify it under the terms of the GNU Lesser General Public // License as published by the Free Software Foundation; either // version 3 of the License, or (at your option) any later version. // pacparser is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU // Lesser General Public License for more details. // You should have received a copy of the GNU Lesser General Public // License along with this library; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA #include #include #include #include #include #ifdef __FreeBSD__ #include #endif #ifdef XP_UNIX #include #include // for AF_INET #include #endif #ifdef _WIN32 #include #include #endif #include "pac_utils.h" #include "pacparser.h" #define MAX_IP_RESULTS 10 #ifdef __GNUC__ # define UNUSED(x) UNUSED_ ## x __attribute__((__unused__)) #else # define UNUSED(x) UNUSED_ ## x #endif static char my_ip_buf[INET6_ADDRSTRLEN+1]; static int my_ip_set = 0; // Default error printer function. static int // Number of characters printed, negative value in case of output error. _default_error_printer(const char *fmt, va_list argp) { return vfprintf(stderr, fmt, argp); } // File level variable to hold error printer function pointer. static pacparser_error_printer error_printer_func = &_default_error_printer; // Set error printer to a user defined function. void pacparser_set_error_printer(pacparser_error_printer func) { error_printer_func = func; } static int print_error(const char *fmt, ...) { int ret; va_list args; va_start(args, fmt); ret = (*error_printer_func)(fmt, args); va_end(args); return ret; } static int _debug(void) { if(getenv("PACPARSER_DEBUG")) return 1; return 0; } // Utility function to read a file into string. static char * // File content in string or NULL if failed. read_file_into_str(const char *filename) { FILE *fptr = fopen(filename, "rb"); if (fptr == NULL) return NULL; if (fseek(fptr, 0L, SEEK_END) != 0) goto error2; long file_size=ftell(fptr); if (file_size == -1) goto error2; if (fseek(fptr, 0L, SEEK_SET) != 0) goto error2; char *str = (char*) malloc(file_size+1); if (str == NULL) goto error2; // Read the file into the string size_t bytes_read = fread(str, 1, file_size, fptr); if (bytes_read != file_size) { free(str); goto error2; } // This check should not be needed but adding this satisfies // sonarlint static analysis, otherwise it complains about tainted // index. if (bytes_read < file_size+1) { str[bytes_read] = '\0'; } fclose(fptr); return str; error2: fclose(fptr); return NULL; } static void print_jserror(JSContext *cx, const char *message, JSErrorReport *report) { print_error("JSERROR: %s:%d:\n %s\n", (report->filename ? report->filename : "NULL"), report->lineno, message); } // DNS Resolve function; used by other routines. // This function is used by dnsResolve, dnsResolveEx, myIpAddress, // myIpAddressEx. static int resolve_host(const char *hostname, char *ipaddr_list, int max_results, int req_ai_family) { struct addrinfo hints; struct addrinfo *result; char ipaddr[INET6_ADDRSTRLEN]; int error; // Truncate ipaddr_list to an empty string. ipaddr_list[0] = '\0'; #ifdef _WIN32 // On windows, we need to initialize the winsock dll first. WSADATA WsaData; WSAStartup(MAKEWORD(2,0), &WsaData); #endif memset(&hints, 0, sizeof(struct addrinfo)); hints.ai_family = req_ai_family; hints.ai_socktype = SOCK_STREAM; error = getaddrinfo(hostname, NULL, &hints, &result); if (error) return error; int i = 0; for(struct addrinfo *ai = result; ai != NULL && i < max_results; ai = ai->ai_next, i++) { getnameinfo(ai->ai_addr, ai->ai_addrlen, ipaddr, sizeof(ipaddr), NULL, 0, NI_NUMERICHOST); if (ipaddr_list[0] == '\0') sprintf(ipaddr_list, "%s", ipaddr); else sprintf(ipaddr_list, "%s;%s", ipaddr_list, ipaddr); } freeaddrinfo(result); #ifdef _WIN32 WSACleanup(); #endif return 0; } // dnsResolve in JS context; not available in core JavaScript. // returns javascript null if not able to resolve. static JSBool // JS_TRUE or JS_FALSE dns_resolve(JSContext *cx, JSObject *UNUSED(o), uintN UNUSED(u), jsval *argv, jsval *rval) { char* name = JS_GetStringBytes(JS_ValueToString(cx, argv[0])); char* out; char ipaddr[INET6_ADDRSTRLEN] = ""; // Return null on failure. if(resolve_host(name, ipaddr, 1, AF_INET)) { *rval = JSVAL_NULL; return JS_TRUE; } out = JS_malloc(cx, strlen(ipaddr) + 1); strcpy(out, ipaddr); JSString *str = JS_NewString(cx, out, strlen(out)); *rval = STRING_TO_JSVAL(str); return JS_TRUE; } // dnsResolveEx in JS context; not available in core JavaScript. // returns javascript null if not able to resolve. static JSBool // JS_TRUE or JS_FALSE dns_resolve_ex(JSContext *cx, JSObject *UNUSED(o), uintN UNUSED(u), jsval *argv, jsval *rval) { char* name = JS_GetStringBytes(JS_ValueToString(cx, argv[0])); char* out; char ipaddr[INET6_ADDRSTRLEN * MAX_IP_RESULTS + MAX_IP_RESULTS] = ""; out = JS_malloc(cx, strlen(ipaddr) + 1); // Return "" on failure. if(resolve_host(name, ipaddr, MAX_IP_RESULTS, AF_UNSPEC)) { strcpy(out, ""); } strcpy(out, ipaddr); JSString *str = JS_NewString(cx, out, strlen(out)); *rval = STRING_TO_JSVAL(str); return JS_TRUE; } // myIpAddress in JS context; not available in core JavaScript. // returns 127.0.0.1 if not able to determine local ip. static JSBool // JS_TRUE or JS_FALSE my_ip(JSContext *cx, JSObject *UNUSED(o), uintN UNUSED(u), jsval *argv, jsval *rval) { char ipaddr[INET6_ADDRSTRLEN]; char* out; if (my_ip_set) // If my (client's) IP address is already set. strcpy(ipaddr, my_ip_buf); else { char name[256]; gethostname(name, sizeof(name)); if (resolve_host(name, ipaddr, 1, AF_INET)) { strcpy(ipaddr, "127.0.0.1"); } } out = JS_malloc(cx, strlen(ipaddr) + 1); strcpy(out, ipaddr); JSString *str = JS_NewString(cx, out, strlen(out)); *rval = STRING_TO_JSVAL(str); return JS_TRUE; } // myIpAddressEx in JS context; not available in core JavaScript. // returns 127.0.0.1 if not able to determine local ip. static JSBool // JS_TRUE or JS_FALSE my_ip_ex(JSContext *cx, JSObject *UNUSED(o), uintN UNUSED(u), jsval *UNUSED(argv), jsval *rval) { char ipaddr[INET6_ADDRSTRLEN * MAX_IP_RESULTS + MAX_IP_RESULTS]; char* out; if (my_ip_set) // If my (client's) IP address is already set. strcpy(ipaddr, my_ip_buf); else { char name[256]; gethostname(name, sizeof(name)); if (resolve_host(name, ipaddr, MAX_IP_RESULTS, AF_UNSPEC)) { strcpy(ipaddr, ""); } } out = JS_malloc(cx, strlen(ipaddr) + 1); strcpy(out, ipaddr); JSString *str = JS_NewString(cx, out, strlen(out)); *rval = STRING_TO_JSVAL(str); return JS_TRUE; } // Define some JS context related variables. static JSRuntime *rt = NULL; static JSContext *cx = NULL; static JSObject *global = NULL; static JSClass global_class = { "global",0, JS_PropertyStub,JS_PropertyStub,JS_PropertyStub,JS_PropertyStub, JS_EnumerateStub,JS_ResolveStub,JS_ConvertStub,JS_FinalizeStub }; // Set my (client's) IP address to a custom value. int pacparser_setmyip(const char *ip) { if (strlen(ip) > INET6_ADDRSTRLEN) { fprintf(stderr, "pacparser_setmyip: IP too long: %s\n", ip); return 0; } strcpy(my_ip_buf, ip); my_ip_set = 1; return 1; } // Deprecated: This function doesn't do anything. // // This function doesn't do anything. Microsoft extensions are now enabled by // default. void pacparser_enable_microsoft_extensions() { return; } // Initialize PAC parser. // // - Initializes JavaScript engine, // - Exports dns_functions (defined above) to JavaScript context. // - Sets error reporting function to print_jserror, // - Evaluates JavaScript code in pacUtils variable defined in pac_utils.h. int // 0 (=Failure) or 1 (=Success) pacparser_init() { jsval rval; char *error_prefix = "pacparser.c: pacparser_init:"; // Initialize JS engine if (!(rt = JS_NewRuntime(8L * 1024L * 1024L)) || !(cx = JS_NewContext(rt, 8192)) || !(global = JS_NewObject(cx, &global_class, NULL, NULL)) || !JS_InitStandardClasses(cx, global)) { print_error("%s %s\n", error_prefix, "Could not initialize JavaScript " "runtime."); return 0; } JS_SetErrorReporter(cx, print_jserror); // Export our functions to Javascript engine if (!JS_DefineFunction(cx, global, "dnsResolve", &dns_resolve, 1, 0)) { print_error("%s %s\n", error_prefix, "Could not define dnsResolve in JS context."); return 0; } if (!JS_DefineFunction(cx, global, "myIpAddress", &my_ip, 0, 0)) { print_error("%s %s\n", error_prefix, "Could not define myIpAddress in JS context."); return 0; } if (!JS_DefineFunction(cx, global, "dnsResolveEx", &dns_resolve_ex, 1, 0)) { print_error("%s %s\n", error_prefix, "Could not define dnsResolveEx in JS context."); return 0; } if (!JS_DefineFunction(cx, global, "myIpAddressEx", &my_ip_ex, 0, 0)) { print_error("%s %s\n", error_prefix, "Could not define myIpAddressEx in JS context."); return 0; } // Evaluate pacUtils. Utility functions required to parse pac files. if (!JS_EvaluateScript(cx, // JS engine context global, // global object pacUtils, // this is defined in pac_utils.h strlen(pacUtils), NULL, // filename (NULL in this case) 1, // line number, used for reporting. &rval)) { print_error("%s %s\n", error_prefix, "Could not evaluate pacUtils defined in pac_utils.h."); return 0; } if (_debug()) print_error("DEBUG: Pacparser Initialized.\n"); return 1; } // Parses the given PAC script string. // // Evaluates the given PAC script string in the JavaScript context created // by pacparser_init. int // 0 (=Failure) or 1 (=Success) pacparser_parse_pac_string(const char *script) { jsval rval; char *error_prefix = "pacparser.c: pacparser_parse_pac_string:"; if (cx == NULL || global == NULL) { print_error("%s %s\n", error_prefix, "Pac parser is not initialized."); return 0; } if (!JS_EvaluateScript(cx, global, script, // Script read from pacfile strlen(script), "PAC script", 1, &rval)) { // If script evaluation failed print_error("%s %s\n", error_prefix, "Failed to evaluate the pac script."); if (_debug()) print_error("DEBUG: Failed to parse the PAC script:\n%s\n", script); return 0; } if (_debug()) print_error("DEBUG: Parsed the PAC script.\n"); return 1; } // Parses the given PAC file. // // reads the given PAC file and evaluates it in the JavaScript context created // by pacparser_init. int // 0 (=Failure) or 1 (=Success) pacparser_parse_pac_file(const char *pacfile) { char *script = NULL; if ((script = read_file_into_str(pacfile)) == NULL) { print_error("pacparser.c: pacparser_parse_pac: %s: %s: %s\n", "Could not read the pacfile: ", pacfile, strerror(errno)); return 0; } int result = pacparser_parse_pac_string(script); if (script != NULL) free(script); if (_debug()) { if(result) print_error("DEBUG: Parsed the PAC file: %s\n", pacfile); else print_error("DEBUG: Could not parse the PAC file: %s\n", pacfile); } return result; } // Parses PAC file (same as pacparser_parse_pac_file) // // (Deprecated) Use pacparser_parse_pac_file instead. int // 0 (=Failure) or 1 (=Success) pacparser_parse_pac(const char *pacfile) { return pacparser_parse_pac_file(pacfile); } // Finds proxy for the given URL and Host. // // If JavaScript engine is intialized and findProxyForURL function is defined, // it evaluates code findProxyForURL(url,host) in JavaScript context and // returns the result. char * // Proxy string or NULL if failed. pacparser_find_proxy(const char *url, const char *host) { char *error_prefix = "pacparser.c: pacparser_find_proxy:"; if (_debug()) print_error("DEBUG: Finding proxy for URL: %s and Host:" " %s\n", url, host); jsval rval; char *script; if (url == NULL || (strcmp(url, "") == 0)) { print_error("%s %s\n", error_prefix, "URL not defined"); return NULL; } if (host == NULL || (strcmp(host,"") == 0)) { print_error("%s %s\n", error_prefix, "Host not defined"); return NULL; } if (cx == NULL || global == NULL) { print_error("%s %s\n", error_prefix, "Pac parser is not initialized."); return NULL; } // Test if findProxyForURL is defined. script = "typeof(findProxyForURL);"; if (_debug()) print_error("DEBUG: Executing JavaScript: %s\n", script); JS_EvaluateScript(cx, global, script, strlen(script), NULL, 1, &rval); if (strcmp("function", JS_GetStringBytes(JS_ValueToString(cx, rval))) != 0) { print_error("%s %s\n", error_prefix, "Javascript function findProxyForURL not defined."); return NULL; } jsval args[2]; args[0] = STRING_TO_JSVAL(JS_NewStringCopyZ(cx, url)); args[1] = STRING_TO_JSVAL(JS_NewStringCopyZ(cx, host)); if (!JS_CallFunctionName(cx, global, "findProxyForURL", 2, args, &rval)) { print_error("%s %s\n", error_prefix, "Problem in executing findProxyForURL."); return NULL; } return JS_GetStringBytes(JS_ValueToString(cx, rval)); } // Destroys JavaSctipt Engine. void pacparser_cleanup() { // Re-initialize config variables. my_ip_set = 0; if (cx) { JS_DestroyContext(cx); cx = NULL; } if (rt) { JS_DestroyRuntime(rt); rt = NULL; } if (!cx && !rt) JS_ShutDown(); global = NULL; if (_debug()) print_error("DEBUG: Pacparser destroyed.\n"); } // Finds proxy for the given PAC file, url and host. // // This function is a wrapper around functions pacparser_init, // pacparser_parse_pac, pacparser_find_proxy and pacparser_cleanup. If you just // want to find out proxy a given set of pac file, url and host, this is the // function to call. char * // Proxy string or NULL if failed. pacparser_just_find_proxy(const char *pacfile, const char *url, const char *host) { char *proxy; char *out; int initialized_here = 0; char *error_prefix = "pacparser.c: pacparser_just_find_proxy:"; if (!global) { if (!pacparser_init()) { print_error("%s %s\n", error_prefix, "Could not initialize pacparser"); return NULL; } initialized_here = 1; } if (!pacparser_parse_pac(pacfile)) { print_error("%s %s %s\n", error_prefix, "Could not parse pacfile", pacfile); if (initialized_here) pacparser_cleanup(); return NULL; } if (!(out = pacparser_find_proxy(url, host))) { print_error("%s %s %s\n", error_prefix, "Could not determine proxy for url", url); if (initialized_here) pacparser_cleanup(); return NULL; } proxy = (char*) malloc(strlen(out) + 1); strcpy(proxy, out); if (initialized_here) pacparser_cleanup(); return proxy; } #define QUOTEME_(x) #x #define QUOTEME(x) QUOTEME_(x) char* pacparser_version(void) { #ifndef VERSION print_error("WARNING: VERSION not defined."); return ""; #endif return QUOTEME(VERSION); } pacparser-1.4.5/src/pacparser.h000066400000000000000000000131211464010763600164260ustar00rootroot00000000000000// Copyright (C) 2007-2023 Manu Garg. // Author: Manu Garg // // This file defines API for pacparser library. // // pacparser is a library that provides methods to parse proxy auto-config // (PAC) files. Please read README file included with this package for more // information about this library. // // pacparser is free software; you can redistribute it and/or // modify it under the terms of the GNU Lesser General Public // License as published by the Free Software Foundation; either // version 3 of the License, or (at your option) any later version. // pacparser is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU // Lesser General Public License for more details. // You should have received a copy of the GNU Lesser General Public // License along with this library; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA #include #ifdef __cplusplus extern "C" { #endif /// @defgroup pacparser pacparser /// @{ /// @brief API for pacparser library, a library to use proxy auto-config (PAC) /// files. See project homepage: http://github.com/pacparser/pacparser /// for more information. /// @author Manu Garg /// @brief Initializes pac parser. /// @returns 0 on failure and 1 on success. /// /// Initializes JavaScript engine and does few basic initializations specific /// to pacparser. int pacparser_init(void); /// @brief Parses the given PAC file. /// @param pacfile PAC file to parse. /// @returns 0 on failure and 1 on success. /// /// Reads the given PAC file and evaluates it in the JavaScript context created /// by pacparser_init. int pacparser_parse_pac_file(const char *pacfile // PAC file to parse ); /// @brief Parses the given PAC script string. /// @param pacstring PAC string to parse. /// @returns 0 on failure and 1 on success. /// /// Evaluates the given PAC script string in the JavaScript context created /// by pacparser_init. int pacparser_parse_pac_string(const char *pacstring // PAC string to parse ); /// @brief Parses the gievn pac file. /// \deprecated Use pacparser_parse_pac_file instead. /// @param pacfile PAC file to parse. /// @returns 0 on failure and 1 on success. /// /// Same as pacparser_parse_pac_file. Included only for backward compatibility. int pacparser_parse_pac(const char *pacfile // PAC file to parse ); /// @brief Finds proxy for the given URL and Host. /// @param url URL to find proxy for. /// @param host Host part of the URL. /// @returns proxy string on success and NULL on error. /// /// Finds proxy for the given URL and Host. This function should be called only /// after pacparser engine has been initialized (using pacparser_init) and pac /// script has been parsed (using pacparser_parse_pac_file or /// pacparser_parse_pac_string). char *pacparser_find_proxy(const char *url, // URL to find proxy for const char *host // Host part of the URL ); /// @brief Finds proxy for the given PAC file, URL and Host. /// @param pacfile PAC file to parse. /// @param url URL to find proxy for. /// @param host Host part of the URL. /// @returns proxy string on success and NULL on error. /// /// This function is a wrapper around functions pacparser_init, /// pacparser_parse_pac_file, pacparser_find_proxy and pacparser_cleanup. If /// you just want to find out proxy for a given set of pac file, url and host, this /// is the function to call. This function takes care of all the initialization /// and cleanup. char *pacparser_just_find_proxy(const char *pacfile, // PAC file const char *url, // URL to find proxy for const char *host // Host part of the URL ); /// @brief Destroys JavaSctipt context. /// /// This function should be called once you're done with using pacparser engine. void pacparser_cleanup(void); /// @brief Sets my IP address. /// @param ip Custom IP address. /// @returns 1 on success and 0 on error. /// /// Sets my IP address to a custom value. This is the IP address returned by /// myIpAddress() javascript function. int pacparser_setmyip(const char *ip // Custom IP address. ); /// @brief Type definition for pacparser_error_printer. typedef int (*pacparser_error_printer)(const char *fmt, // printf format va_list argp // Variadic arg list ); /// @brief Sets error printing function. /// @param func Printing function. /// /// Sets error variadic-argument printing function. If not set the messages /// are printed to stderr. If messages begin with DEBUG: or WARNING:, /// they are not fatal error messages, otherwise they are. /// May be called before pacparser_init(). void pacparser_set_error_printer(pacparser_error_printer func // Printing function ); /// @brief (Deprecated) Enable Microsoft IPv6 PAC extensions. /// /// Deprecated. IPv6 extension (*Ex functions) are enabled by default now. void pacparser_enable_microsoft_extensions(void); /// @brief Returns pacparser version. /// @returns version string if version defined, "" otherwise. /// /// Version string is determined at the time of build. If built from a released /// package, version corresponds to the latest release (git) tag. If built from the /// repository, it corresponds to the head revision of the repo. char* pacparser_version(void); #ifdef __cplusplus } #endif /// @} pacparser-1.4.5/src/pactester.c000066400000000000000000000173211464010763600164410ustar00rootroot00000000000000// Copyright (C) 2008-2023 Manu Garg. // Author: Manu Garg // // This file implements pactester using pacparser. // // pacparser is a library that provides methods to parse proxy auto-config // (PAC) files. Please read README file included with this package for more // information about this library. // // pacparser is free software; you can redistribute it and/or // modify it under the terms of the GNU Lesser General Public // License as published by the Free Software Foundation; either // version 3 of the License, or (at your option) any later version. // pacparser is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU // Lesser General Public License for more details. // You should have received a copy of the GNU Lesser General Public // License along with this library; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA #include "pacparser.h" #include #include #include #include #define STREQ(s1, s2) (strcmp((s1), (s2)) == 0) #define LINEMAX 4096 // Max length of any line read from text files (4 KiB) #define PACMAX (1024 * 1024) // Max size of the PAC script (1 MiB) __attribute__((noreturn)) void usage(const char *progname) { fprintf(stderr, "\nUsage: %s <-p pacfile> <-u url> [-h host] " "[-c client_ip] [-e]", progname); fprintf(stderr, "\n %s <-p pacfile> <-f urlslist> " "[-c client_ip] [-e]\n", progname); fprintf(stderr, "\nOptions:\n"); fprintf(stderr, " -p pacfile : PAC file to test (specify '-' to read " "from standard input)\n"); fprintf(stderr, " -u url : URL to test for\n"); fprintf(stderr, " -h host : Host part of the URL\n"); fprintf(stderr, " -c client_ip : client IP address (as returned by " "myIpAddres() function\n"); fprintf(stderr, " in PAC files), defaults to IP address " "on which it is running.\n"); fprintf(stderr, " -e : Deprecated: IPv6 extensions are enabled" "by default now.\n"); fprintf(stderr, " -f urlslist : a file containing list of URLs to be " "tested.\n"); fprintf(stderr, " -v : print version and exit\n"); exit(1); } char *get_host_from_url(const char *url) { // copy url to a pointer that we'll use to seek through the string. char *p = strdup(url); char *q = p; // Move to : while (*p != ':' && *p != '\0') p++; if (p[0] == '\0'|| // We reached end without hitting : p[1] != '/' || p[2] != '/' // Next two characters are not // ) { free(q); fprintf(stderr, "pactester.c: Not a proper URL\n"); return NULL; } p = p + 3; // Get past '://' // Host part starts from here. char *host = p; if (*p == '\0' || *p == '/' || *p == ':') { // If host part is null. free(q); fprintf(stderr, "pactester.c: Not a proper URL\n"); return NULL; } // Seek until next /, : or end of string. while (*p != '/' && *p != ':' && *p != '\0') p++; *p = '\0'; return host; } int main(int argc, char* argv[]) { char *pacfile = NULL, *url = NULL, *host = NULL, *urlslist = NULL, *client_ip = NULL; if (argv[1] && (STREQ(argv[1], "--help") || STREQ(argv[1], "--helpshort"))) { usage(argv[0]); } signed char c; while ((c = getopt(argc, argv, "evp:u:h:f:c:")) != -1) switch (c) { case 'v': printf("%s\n", pacparser_version()); return 0; case 'p': pacfile = optarg; break; case 'u': url = optarg; break; case 'h': host = optarg; break; case 'f': urlslist = optarg; break; case 'c': client_ip = optarg; break; case 'e': break; case '?': usage(argv[0]); /* fallthrough */ default: abort (); } if (!pacfile) { fprintf(stderr, "pactester.c: You didn't specify the PAC file\n"); usage(argv[0]); } if (!url && !urlslist) { fprintf(stderr, "pactester.c: You didn't specify the URL\n"); usage(argv[0]); } // Initialize pacparser. if (!pacparser_init()) { fprintf(stderr, "pactester.c: Could not initialize pacparser\n"); return 1; } // Read pacfile from stdin. if (STREQ("-", pacfile)) { char *script; size_t script_size = 1; // for the null terminator char buffer[LINEMAX]; script = (char *) malloc(sizeof(char) * LINEMAX); if (script == NULL) { perror("pactetser.c: Failed to allocate the memory for the script"); return 1; } script[0] = '\0'; // Null terminate to prepare for strcat while (fgets(buffer, LINEMAX, stdin)) { if (strlen(buffer) == 0) break; script_size += strlen(buffer); if (script_size > PACMAX) { fprintf(stderr, "Input file is too big. Maximum allowed size is: %d", PACMAX); exit(1); } script = realloc(script, script_size); if (script == NULL) { perror("pactester.c: Failed to allocate the memory for the script"); exit(1); } strcat(script, buffer); } if (ferror(stdin)) { free(script); perror("pactester.c: Error reading from stdin"); return 1; } if (!pacparser_parse_pac_string(script)) { fprintf(stderr, "pactester.c: Could not parse the pac script: %s\n", script); pacparser_cleanup(); exit(1); } free(script); } else { if (!pacparser_parse_pac_file(pacfile)) { fprintf(stderr, "pactester.c: Could not parse the pac file: %s\n", pacfile); pacparser_cleanup(); exit(1); } } if (client_ip && !pacparser_setmyip(client_ip)) { fprintf(stderr, "pactester.c: Error setting client IP\n"); pacparser_cleanup(); exit(1); } char *proxy; if (url) { // If the host was not explicitly given, get it from the URL. // If that fails, return with error (the get_host_from_url() // function will print a proper error message in that case). host = host ? host: get_host_from_url(url); if (!host) { exit(1); } proxy = pacparser_find_proxy(url, host); if (proxy == NULL) { fprintf(stderr, "pactester.c: %s %s.\n", "Problem in finding proxy for", url); pacparser_cleanup(); exit(1); } printf("%s\n", proxy); exit(0); } if (urlslist) { char line[LINEMAX]; FILE *fp; if (!(fp = fopen(urlslist, "r"))) { fprintf(stderr, "pactester.c: Could not open urlslist: %s", urlslist); pacparser_cleanup(); exit(1); } while (fgets(line, sizeof(line), fp)) { char *url = line; // Remove spaces from the beginning. while (*url == ' ' || *url == '\t') url++; // Skip comment lines. if (*url == '#') { printf("%s", url); continue; } char *urlend = url; while (*urlend != '\r' && *urlend != '\n' && *urlend != '\0' && *urlend != ' ' && *urlend != '\t') urlend++; // keep moving till you hit space or end of string *urlend = '\0'; if (!(host = get_host_from_url(url)) ) continue; proxy = NULL; proxy = pacparser_find_proxy(url, host); if (proxy == NULL) { fprintf(stderr, "pactester.c: %s %s.\n", "Problem in finding proxy for", url); pacparser_cleanup(); exit(1); } if (proxy) printf("%s : %s\n", url, proxy); } fclose(fp); exit(0); } pacparser_cleanup(); return 0; } pacparser-1.4.5/src/pymod/000077500000000000000000000000001464010763600154275ustar00rootroot00000000000000pacparser-1.4.5/src/pymod/MANIFEST.in000066400000000000000000000000531464010763600171630ustar00rootroot00000000000000include pacparser_py.c include pacparser/* pacparser-1.4.5/src/pymod/install_win32.py000066400000000000000000000006541464010763600204760ustar00rootroot00000000000000import shutil import sys from distutils import sysconfig from __future__ import print_function def main(): if sys.platform == 'win32': shutil.rmtree('%s\\pacparser' % sysconfig.get_python_lib(), ignore_errors=True) shutil.copytree('pacparser', '%s\\pacparser' % sysconfig.get_python_lib()) else: print('This script should be used only on Win32 systems.') if __name__ == '__main__': main() pacparser-1.4.5/src/pymod/pacparser/000077500000000000000000000000001464010763600174075ustar00rootroot00000000000000pacparser-1.4.5/src/pymod/pacparser/__init__.py000066400000000000000000000065201464010763600215230ustar00rootroot00000000000000# Copyright (C) 2007 Manu Garg. # Author: Manu Garg # # pacparser is a library that provides methods to parse proxy auto-config # (PAC) files. Please read README file included with this package for more # information about this library. # # pacparser is free software; you can redistribute it and/or # modify it under the terms of the GNU Lesser General Public # License as published by the Free Software Foundation; either # version 2.1 of the License, or (at your option) any later version. # pacparser is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU # Lesser General Public License for more details. # You should have received a copy of the GNU Lesser General Public # License along with this library; if not, write to the Free Software # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA """ Python module to parse pac files. Look at project's homepage http://github.com/pacparser/pacparser for more information. """ __author__ = 'manugarg@gmail.com (Manu Garg)' __copyright__ = 'Copyright (C) 2008 Manu Garg' __license__ = 'LGPL' from pacparser import _pacparser import os import re import sys _URL_REGEX = re.compile('^[^:]*:\/\/([^\/:]+)') class URLError(Exception): def __init__(self, url): super(URLError, self).__init__('URL: {} is not valid'.format(url)) self.url = url def init(): """ Initializes pacparser engine. """ _pacparser.init() def parse_pac(pacfile): """ (Deprecated) Same as parse_pac_file. """ parse_pac_file(pacfile) def parse_pac_file(pacfile): """ Reads the pacfile and evaluates it in the Javascript engine created by init(). """ try: with open(pacfile) as f: pac_script = f.read() _pacparser.parse_pac_string(pac_script) except IOError: raise IOError('Could not read the pacfile: {}'.format(pacfile)) def parse_pac_string(pac_script): """ Evaluates pac_script in the Javascript engine created by init(). """ _pacparser.parse_pac_string(pac_script) def find_proxy(url, host=None): """ Finds proxy string for the given url and host. If host is not defined, it's extracted from the url. """ if host is None: m = _URL_REGEX.match(url) if not m: raise URLError(url) if len(m.groups()) == 1: host = m.groups()[0] else: raise URLError(url) return _pacparser.find_proxy(url, host) def version(): """ Returns the compiled pacparser version. """ return _pacparser.version() def cleanup(): """ Destroys pacparser engine. """ _pacparser.cleanup() def just_find_proxy(pacfile, url, host=None): """ This function is a wrapper around init, parse_pac, find_proxy and cleanup. This is the function to call if you want to find proxy just for one url. """ if not os.path.isfile(pacfile): raise IOError('Pac file does not exist: {}'.format(pacfile)) init() parse_pac(pacfile) proxy = find_proxy(url,host) cleanup() return proxy def setmyip(ip_address): """ Set my ip address. This is the IP address returned by myIpAddress() """ _pacparser.setmyip(ip_address) def enable_microsoft_extensions(): """ Enables Microsoft PAC extensions (dnsResolveEx, isResolvableEx, myIpAddressEx). """ _pacparser.enable_microsoft_extensions() pacparser-1.4.5/src/pymod/pacparser_py.c000066400000000000000000000124431464010763600202670ustar00rootroot00000000000000// Copyright (C) 2007 Manu Garg. // Author: Manu Garg // // pacparser is a library that provides methods to parse proxy auto-config // (PAC) files. Please read README file included with this package for more // information about this library. // // pacparser is free software; you can redistribute it and/or // modify it under the terms of the GNU Lesser General Public // License as published by the Free Software Foundation; either // version 2.1 of the License, or (at your option) any later version. // pacparser is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU // Lesser General Public License for more details. // You should have received a copy of the GNU Lesser General Public // License along with this library; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA #include #include "pacparser.h" // PyMODINIT_FUNC macro is not defined on python < 2.3. Take care of that. #ifndef PyMODINIT_FUNC /* declarations for DLL import/export */ #define PyMODINIT_FUNC void #endif // Py_RETURN_NONE macro is not defined on python < 2.4. Take care of that. #ifndef Py_RETURN_NONE #define Py_RETURN_NONE return Py_INCREF(Py_None), Py_None #endif #if PY_MAJOR_VERSION >= 3 #define MOD_ERROR_VAL NULL #define MOD_SUCCESS_VAL(val) val #define MOD_INIT(name) PyMODINIT_FUNC PyInit_##name(void) #define MOD_DEF(ob, name, doc, methods) \ static struct PyModuleDef moduledef = { \ PyModuleDef_HEAD_INIT, name, doc, -1, methods, }; \ ob = PyModule_Create(&moduledef); #else #define MOD_ERROR_VAL #define MOD_SUCCESS_VAL(val) #define MOD_INIT(name) void init##name(void) #define MOD_DEF(ob, name, doc, methods) \ ob = Py_InitModule3(name, methods, doc); #endif static PyObject *PacparserError; // Initialize PAC parser. // // - Initializes JavaScript engine, // - Exports dns_functions (defined above) to JavaScript context. // - Sets error reporting function to print_error, // - Evaluates JavaScript code in pacUtils variable defined in pac_utils.h. static PyObject * // 0 (=Failure) or 1 (=Success) py_pacparser_init(PyObject *self, PyObject *args) { if(pacparser_init()) Py_RETURN_NONE; else { PyErr_SetString(PacparserError, "Could not initialize pacparser"); return NULL; } } // Parses the PAC script string. // // Evaluates the PAC script string in the JavaScript context created by // pacparser_init. static PyObject * // 0 (=Failure) or 1 (=Success) py_pacparser_parse_pac_string(PyObject *self, PyObject *args) { const char *pac_script; if (!PyArg_ParseTuple(args, "s", &pac_script)) return NULL; if (pacparser_parse_pac_string(pac_script)) Py_RETURN_NONE; else { PyErr_SetString(PacparserError, "Could not parse pac script string"); return NULL; } } // Finds proxy for the given URL and Host. // // Evaluates FindProxyForURL(url,host) in the JavaScript context and returns // the result. static PyObject * // Proxy string or NULL if failed. py_pacparser_find_proxy(PyObject *self, PyObject *args) { char *proxy; const char *url; const char *host; if (!PyArg_ParseTuple(args, "ss", &url, &host)) return NULL; if(!(proxy = pacparser_find_proxy(url, host))) { PyErr_SetString(PacparserError, "Could not find proxy"); return NULL; } return Py_BuildValue("s", proxy); } // Return pacparser version. static PyObject * // Version string. py_pacparser_version(PyObject *self, PyObject *args) { return Py_BuildValue("s", pacparser_version()); } // Destroys JavaSctipt Engine. static PyObject * py_pacparser_cleanup(PyObject *self, PyObject *args) { pacparser_cleanup(); Py_RETURN_NONE; } // Sets local ip to the given argument. static PyObject * py_pacparser_setmyip(PyObject *self, PyObject *args) { const char *ip; if (!PyArg_ParseTuple(args, "s", &ip)) return NULL; pacparser_setmyip(ip); Py_RETURN_NONE; } // Enables Microsoft extensions. static PyObject * py_pacparser_enable_microsoft_extensions(PyObject *self, PyObject *args) { pacparser_enable_microsoft_extensions(); Py_RETURN_NONE; } static PyMethodDef PpMethods[] = { {"init", py_pacparser_init, METH_VARARGS, "initialize pacparser"}, {"parse_pac_string", py_pacparser_parse_pac_string, METH_VARARGS, "parses pac script string"}, {"find_proxy", py_pacparser_find_proxy, METH_VARARGS, "returns proxy string"}, {"version", py_pacparser_version, METH_VARARGS, "returns pacparser version"}, {"cleanup", py_pacparser_cleanup, METH_VARARGS, "destroy pacparser engine"}, {"setmyip", py_pacparser_setmyip, METH_VARARGS, "set my ip address"}, {"enable_microsoft_extensions", py_pacparser_enable_microsoft_extensions, METH_VARARGS, "enable Microsoft extensions"}, {NULL, NULL, 0, NULL} }; MOD_INIT(_pacparser) { PyObject *m; MOD_DEF(m, "_pacparser", NULL, PpMethods) if(m == NULL) return MOD_ERROR_VAL; PacparserError = PyErr_NewException("pacparser.error", NULL, NULL); Py_INCREF(PacparserError); PyModule_AddObject(m, "error", PacparserError); return MOD_SUCCESS_VAL(m); } pacparser-1.4.5/src/pymod/setup.py000066400000000000000000000124231464010763600171430ustar00rootroot00000000000000# Copyright (C) 2007-2023 Manu Garg. # Author: Manu Garg # # pacparser is a library that provides methods to parse proxy auto-config # (PAC) files. Please read README file included with this package for more # information about this library. # # pacparser is free software; you can redistribute it and/or # modify it under the terms of the GNU Lesser General Public # License as published by the Free Software Foundation; either # version 2.1 of the License, or (at your option) any later version. # pacparser is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU # Lesser General Public License for more details. # You should have received a copy of the GNU Lesser General Public # License along with this library; if not, write to the Free Software # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, # USA. """ Wrapper script around python module Makefiles. This script take care of identifying python setup and setting up some environment variables needed by Makefiles. """ import glob import os import platform import re import setuptools import shutil import subprocess import sys from unittest.mock import patch def setup_dir(): return os.path.dirname(os.path.join(os.getcwd(), sys.argv[0])) def module_path(): py_ver = "*".join([str(x) for x in sys.version_info[0:2]]) print("Python version: %s" % py_ver) builddir = os.path.join(setup_dir(), "build") print("Build dir: %s" % builddir) print(os.listdir(builddir)) return glob.glob(os.path.join(builddir, "lib*%s*" % py_ver))[0] def sanitize_version(ver): ver = ver.strip() # Strip first 'v' and last part from git provided versions. # For example, v1.3.8-12-g231 becomes v1.3.8-12. ver = re.sub(r"^v?([\d]{1,3}\.[\d]{1,3}\.[\d]{1,3}(-[\d]{1,3})).*$", "\\1", ver) # 1.3.8-12 becomes 1.3.8.dev12 return ver.replace("-", ".dev") def git_version(): return sanitize_version( subprocess.check_output( "git describe --always --tags --candidate=100".split(" "), text=True ) ) def pacparser_version(): if ( subprocess.call("git rev-parse --git-dir".split(" "), stderr=subprocess.DEVNULL) == 0 ): return git_version() # Check if we have version.mk. It's added in the manual release tarball. version_file = os.path.join(setup_dir(), "..", "version.mk") if os.path.exists(version_file): with open(version_file) as f: return sanitize_version(f.read().replace("VERSION=", "")) return sanitize_version(os.environ.get("PACPARSER_VERSION", "1.0.0")) class DistCmd(setuptools.Command): """Build pacparser python distribution.""" description = "Build pacparser python distribution." user_options = [] def initialize_options(self): # Override to do nothing. pass def finalize_options(self): # Override to do nothing. pass def run(self): py_ver = ".".join([str(x) for x in sys.version_info[0:2]]) pp_ver = pacparser_version() mach = platform.machine().lower() if mach == "x86_64": mach = "amd64" dist = "pacparser-python%s-%s-%s-%s" % ( py_ver.replace(".", ""), pp_ver, platform.system().lower(), mach, ) if os.path.exists(dist): shutil.rmtree(dist) os.mkdir(dist) shutil.copytree( os.path.join(module_path(), "pacparser"), dist + "/pacparser", ignore=shutil.ignore_patterns("*pycache*"), ) @patch("setuptools._distutils.cygwinccompiler.get_msvcr") def main(patched_func): python_home = os.path.dirname(sys.executable) extra_objects = [] obj_search_path = { "pacparser.o": ["..", "."], "libjs.a": ["../spidermonkey", "."], } for obj, paths in obj_search_path.items(): for path in paths: if os.path.exists(os.path.join(path, obj)): extra_objects.append(os.path.join(path, obj)) break libraries = [] extra_link_args = [] if sys.platform == "win32": import distutils.cygwinccompiler distutils.cygwinccompiler.get_msvcr = lambda: ["vcruntime140"] extra_objects = ["../pacparser.o", "../spidermonkey/js.lib"] libraries = ["ws2_32"] extra_link_args = ["-static-libgcc", "-L" + python_home] pacparser_module = setuptools.Extension( "_pacparser", include_dirs=[".."], sources=["pacparser_py.c"], libraries=libraries, extra_link_args=extra_link_args, extra_objects=extra_objects, ) setuptools.setup( cmdclass={ "dist": DistCmd, }, name="pacparser", version=pacparser_version(), description="Pacparser package", author="Manu Garg", author_email="manugarg@gmail.com", url="https://github.com/manugarg/pacparser", long_description=("python library to parse proxy auto-config (PAC) files."), license="LGPL", ext_package="pacparser", ext_modules=[pacparser_module], py_modules=["pacparser.__init__"], ) if __name__ == "__main__": main() pacparser-1.4.5/src/spidermonkey/000077500000000000000000000000001464010763600170105ustar00rootroot00000000000000pacparser-1.4.5/src/spidermonkey/Makefile000066400000000000000000000026111464010763600204500ustar00rootroot00000000000000# Copyright (C) 2007 Manu Garg. # Author: Manu Garg # # Makefile for pacparser. Please read README file included with this package # for more information about pacparser. # # pacparser is free software; you can redistribute it and/or # modify it under the terms of the GNU Lesser General Public # License as published by the Free Software Foundation; either # version 3 of the License, or (at your option) any later version. # pacparser is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU # Lesser General Public License for more details. # You should have received a copy of the GNU Lesser General Public # License along with this library; if not, write to the Free Software # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA # Include config.mk to get the variable $(OBJDIR). # We need to create $(OBJDIR) first to be able to build libjs.a alone. DEPTH = js/src include js/src/config.mk jsapi: js-buildstamp jslib: js-buildstamp cd js/src find . -name "libjs.a" -exec cp {} . \; js-buildstamp: mkdir -p js/src/$(OBJDIR) CFLAGS="$(SMCFLAGS)" $(MAKE) -C js/src -f Makefile.ref libjs.a find js/src -name "jsautocfg.h" -exec cp {} js/src \; touch js-buildstamp clean: rm -rf js/src/$(OBJDIR) rm -f libjs.a js-buildstamp pacparser-1.4.5/src/spidermonkey/Makefile.win32000066400000000000000000000033711464010763600214150ustar00rootroot00000000000000# Project: fdlibm ifeq ($(OS),Windows_NT) RM = del /Q /F CP = copy /Y ifdef ComSpec SHELL := $(ComSpec) endif ifdef COMSPEC SHELL := $(COMSPEC) endif else RM = rm -rf CP = cp -f endif CC = gcc.exe JS_SRCDIR = js/src JS_OBJECTS = \ $(JS_SRCDIR)/jsapi.o \ $(JS_SRCDIR)/jsarena.o \ $(JS_SRCDIR)/jsarray.o \ $(JS_SRCDIR)/jsatom.o \ $(JS_SRCDIR)/jsbool.o \ $(JS_SRCDIR)/jscntxt.o \ $(JS_SRCDIR)/jsdate.o \ $(JS_SRCDIR)/jsdbgapi.o \ $(JS_SRCDIR)/jsdhash.o \ $(JS_SRCDIR)/jsdtoa.o \ $(JS_SRCDIR)/jsemit.o \ $(JS_SRCDIR)/jsexn.o \ $(JS_SRCDIR)/jsfun.o \ $(JS_SRCDIR)/jsgc.o \ $(JS_SRCDIR)/jshash.o \ $(JS_SRCDIR)/jsiter.o \ $(JS_SRCDIR)/jsinterp.o \ $(JS_SRCDIR)/jslock.o \ $(JS_SRCDIR)/jslog2.o \ $(JS_SRCDIR)/jslong.o \ $(JS_SRCDIR)/jsmath.o \ $(JS_SRCDIR)/jsnum.o \ $(JS_SRCDIR)/jsobj.o \ $(JS_SRCDIR)/jsopcode.o \ $(JS_SRCDIR)/jsparse.o \ $(JS_SRCDIR)/jsprf.o \ $(JS_SRCDIR)/jsregexp.o \ $(JS_SRCDIR)/jsscan.o \ $(JS_SRCDIR)/jsscope.o \ $(JS_SRCDIR)/jsscript.o \ $(JS_SRCDIR)/jsstr.o \ $(JS_SRCDIR)/jsutil.o \ $(JS_SRCDIR)/jsxml.o \ $(JS_SRCDIR)/jsxdrapi.o \ $(JS_SRCDIR)/prmjtime.o CFLAGS = -D_IEEE_LIBM -DEXPORT_JS_API -DWIN32 -D_MINGW -D_WINDOWS -DXP_WIN -s all: js.lib %.o: %.c js/src/jsautokw.h $(CC) -c $(CFLAGS) -o $@ $< js/src/jsautokw.h: ifeq ($(wildcard js),) $(error JS source directory not found. Extract $(wildcard js-*.tar.gz) tarball using tool of your choice. Possible options are 7z, WinRAR, WinZip.) endif $(CC) -o jskwgen js/src/jskwgen.c jskwgen > js/src/jsautokw.h $(RM) jskwgen.exe js.lib: $(JS_OBJECTS) ar r js.lib $(JS_OBJECTS) ranlib js.lib clean: $(RM) js\src\*.o $(RM) js\src\jsautokw.h $(RM) *.lib pacparser-1.4.5/src/spidermonkey/README.md000066400000000000000000000001341464010763600202650ustar00rootroot00000000000000This directory contains a trimmed down version of Mozilla SpiderMonkey library version 1.7. pacparser-1.4.5/src/spidermonkey/js/000077500000000000000000000000001464010763600174245ustar00rootroot00000000000000pacparser-1.4.5/src/spidermonkey/js/README000066400000000000000000000003331464010763600203030ustar00rootroot000000000000001. The latest release notes for SpiderMonkey can be found at: http://www.mozilla.org/js/spidermonkey/release-notes/ 2. js/jsd contains code for debugging support for the C-based JavaScript engine in js/src. pacparser-1.4.5/src/spidermonkey/js/src/000077500000000000000000000000001464010763600202135ustar00rootroot00000000000000pacparser-1.4.5/src/spidermonkey/js/src/.cvsignore000066400000000000000000000001041464010763600222060ustar00rootroot00000000000000*.pdb *.ncb *.opt *.plg Debug Release Makefile jscpucfg jsautocfg.h pacparser-1.4.5/src/spidermonkey/js/src/Makefile.in000066400000000000000000000226261464010763600222700ustar00rootroot00000000000000# -*- Mode: makefile -*- # # ***** BEGIN LICENSE BLOCK ***** # Version: MPL 1.1/GPL 2.0/LGPL 2.1 # # The contents of this file are subject to the Mozilla Public License Version # 1.1 (the "License"); you may not use this file except in compliance with # the License. You may obtain a copy of the License at # http://www.mozilla.org/MPL/ # # Software distributed under the License is distributed on an "AS IS" basis, # WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License # for the specific language governing rights and limitations under the # License. # # The Original Code is Mozilla Communicator client code, released # March 31, 1998. # # The Initial Developer of the Original Code is # Netscape Communications Corporation. # Portions created by the Initial Developer are Copyright (C) 1998 # the Initial Developer. All Rights Reserved. # # Contributor(s): # # Alternatively, the contents of this file may be used under the terms of # either of the GNU General Public License Version 2 or later (the "GPL"), # or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), # in which case the provisions of the GPL or the LGPL are applicable instead # of those above. If you wish to allow use of your version of this file only # under the terms of either the GPL or the LGPL, and not to allow others to # use your version of this file under the terms of the MPL, indicate your # decision by deleting the provisions above and replace them with the notice # and other provisions required by the GPL or the LGPL. If you do not delete # the provisions above, a recipient may use your version of this file under # the terms of any one of the MPL, the GPL or the LGPL. # # ***** END LICENSE BLOCK ***** DEPTH = ../.. topsrcdir = @top_srcdir@ srcdir = @srcdir@ VPATH = @srcdir@ include $(DEPTH)/config/autoconf.mk MODULE = js LIBRARY_NAME = mozjs LIB_IS_C_ONLY = 1 GRE_MODULE = 1 ifeq (,$(filter-out WINNT WINCE,$(OS_ARCH))) LIBRARY_NAME = js$(MOZ_BITS)$(VERSION_NUMBER) RESFILE = js$(MOZ_BITS)40.res endif PACKAGE_FILE = js.pkg # JavaScript must be built shared, even for static builds, as it is used by # other modules which are always built shared. Failure to do so results in # the js code getting copied into xpinstall and jsd as well as mozilla-bin, # and then the static data cells used for locking no longer work. ifndef JS_STATIC_BUILD FORCE_SHARED_LIB = 1 endif CSRCS = \ jsapi.c \ jsarena.c \ jsarray.c \ jsatom.c \ jsbool.c \ jscntxt.c \ jsdate.c \ jsdbgapi.c \ jsdhash.c \ jsdtoa.c \ jsemit.c \ jsexn.c \ jsfun.c \ jsgc.c \ jshash.c \ jsinterp.c \ jsiter.c \ jslock.c \ jslog2.c \ jslong.c \ jsmath.c \ jsnum.c \ jsobj.c \ jsopcode.c \ jsparse.c \ jsprf.c \ jsregexp.c \ jsscan.c \ jsscope.c \ jsscript.c \ jsstr.c \ jsutil.c \ jsxdrapi.c \ jsxml.c \ prmjtime.c \ $(NULL) EXPORTS = \ jsautocfg.h \ jsautokw.h \ js.msg \ jsapi.h \ jsarray.h \ jsarena.h \ jsatom.h \ jsbit.h \ jsbool.h \ jsclist.h \ jscntxt.h \ jscompat.h \ jsconfig.h \ jsdate.h \ jsdbgapi.h \ jsdhash.h \ jsemit.h \ jsfun.h \ jsgc.h \ jshash.h \ jsinterp.h \ jsiter.h \ jslock.h \ jslong.h \ jsmath.h \ jsnum.h \ jsobj.h \ jsopcode.tbl \ jsopcode.h \ jsosdep.h \ jsotypes.h \ jsparse.h \ jsprf.h \ jsproto.tbl \ jsprvtd.h \ jspubtd.h \ jsregexp.h \ jsscan.h \ jsscope.h \ jsscript.h \ jsstddef.h \ jsstr.h \ jstypes.h \ jsutil.h \ jsxdrapi.h \ jsxml.h \ $(NULL) ifeq (,$(filter-out WINNT WINCE,$(OS_ARCH))) EXPORTS += jscpucfg.h endif JS_SAFE_ARENA = 1 DASH_R = -r include $(topsrcdir)/config/config.mk EXTRA_DSO_LDOPTS += $(NSPR_LIBS) # When using gcc the assembly is inlined in the C-file (see jslock.c) ifeq ($(OS_ARCH),SunOS) ifneq ($(OS_TEST),i86pc) ifndef GNU_CC ASFILES = lock_$(OS_ARCH).s endif endif endif ifndef BUILD_OPT MOCHAFILE = 1 endif ifndef NSBUILDROOT JSJAVA_STUBHEADERS = \ -I$(topsrcdir)/sun-java/include/_gen \ -I$(topsrcdir)/sun-java/netscape/javascript/_jri \ -I$(topsrcdir)/sun-java/netscape/security/_jri else JSJAVA_STUBHEADERS = -I$(JRI_GEN_DIR) -I$(JDK_GEN_DIR) endif JSJAVA_CFLAGS = \ -I$(topsrcdir)/sun-java/md-include \ -I$(topsrcdir)/sun-java/include \ $(JSJAVA_STUBHEADERS) # Define keyword generator before rules.mk, see bug 323979 comment 50 HOST_SIMPLE_PROGRAMS += host_jskwgen$(HOST_BIN_SUFFIX) GARBAGE += jsautokw.h host_jskwgen$(HOST_BIN_SUFFIX) include $(topsrcdir)/config/rules.mk DEFINES += -DEXPORT_JS_API INCLUDES += -I$(srcdir) # MSVC '-Gy' cc flag and '/OPT:REF' linker flag cause JS_GetArgument and # JS_GetLocalVariable to be folded to the same address by the linker, # leading to a crash on startup. See bug 151066. So, in optimized builds, # add the /OPT:NOICF flag, which turns off 'identical COMDAT folding'. # # N.B.: 'identical COMDAT folding' that folds functions whose addresses # are taken violates the ISO C and C++ standards. ifndef MOZ_DEBUG ifeq (_WINNT,$(GNU_CC)_$(OS_ARCH)) LDFLAGS += -OPT:NOICF endif endif GARBAGE += jscpucfg.o jsautocfg.h jsautocfg.tmp jscpucfg ifneq (,$(CROSS_COMPILE)$(filter-out WINNT,$(OS_ARCH))) TARGETS += jscpucfg$(HOST_BIN_SUFFIX) endif ifdef JS_SAFE_ARENA DEFINES += -DJS_USE_SAFE_ARENA endif ifdef JS_THREADSAFE DEFINES += -DJS_THREADSAFE endif ifdef JS_NO_THIN_LOCKS DEFINES += -DJS_USE_ONLY_NSPR_LOCKS endif ifdef JS_VERSION DEFINES += -DJS_VERSION=$(JS_VERSION) endif ifneq ($(findstring -L,$(NSPR_LIBS)),) NSPR_STATIC_PATH = $(subst -L,,$(findstring -L,$(NSPR_LIBS))) else NSPR_STATIC_PATH = $(DIST)/lib endif LDFLAGS += $(pathsubst -l%,$(NSPR_STATIC_PATH)/%.a,$(NSPR_LIBS)) # BeOS and HP-UX do not require the extra linking of "-lm" ifeq (,$(filter BeOS HP-UX WINNT WINCE OpenVMS,$(OS_ARCH))) LDFLAGS += -lm endif # Prevent floating point errors caused by VC++ optimizations ifeq ($(OS_ARCH)_$(GNU_CC),WINNT_) ifeq (,$(filter-out 1200 1300 1310,$(_MSC_VER))) CFLAGS += -Op else CFLAGS += -fp:precise endif endif # WINNT ifeq ($(OS_ARCH),FreeBSD) LDFLAGS += -pthread endif ifeq ($(OS_ARCH),IRIX) ifdef USE_N32 DASH_R += -n32 endif endif ifeq ($(OS_ARCH),Linux) LDFLAGS += -ldl endif ifeq ($(OS_ARCH),OSF1) LDFLAGS += -lc_r endif ifeq ($(OS_ARCH),SunOS) ifeq ($(TARGET_CPU),sparc) ifdef JS_ULTRASPARC_OPTS DEFINES += -DULTRA_SPARC ifdef GNU_CC CFLAGS += -Wa,-xarch=v8plus,-DULTRA_SPARC,-P,-L,-D_ASM,-D__STDC__=0 CXXFLAGS += -Wa,-xarch=v8plus,-DULTRA_SPARC,-P,-L,-D_ASM,-D__STDC__=0,-K,PIC else ASFLAGS += -xarch=v8plus -DULTRA_SPARC -P -L -D_ASM -D__STDC__=0 -K PIC endif # GNU_CC endif # JS_ULTRASPARC_OPTS endif ifeq ($(OS_RELEASE),4.1) LDFLAGS += -ldl -lnsl else LDFLAGS += -lposix4 -ldl -lnsl -lsocket endif endif ifeq ($(OS_ARCH),IRIX) ifndef GNU_CC _COMPILE_CFLAGS = $(patsubst -O%,-O1,$(COMPILE_CFLAGS)) jsapi.o jsarena.o jsarray.o jsatom.o jsemit.o jsfun.o jsinterp.o jsregexp.o jsparse.o jsopcode.o jsscript.o: %.o: %.c Makefile.in $(REPORT_BUILD) @$(MAKE_DEPS_AUTO) $(CC) -o $@ -c $(_COMPILE_CFLAGS) $< endif endif # An AIX Optimization bug causes PR_dtoa() & JS_dtoa to produce wrong result. # This suppresses optimization for this single compilation unit. ifeq ($(OS_ARCH),AIX) jsatom.o: jsatom.c Makefile.in $(REPORT_BUILD) @$(MAKE_DEPS_AUTO) $(CC) -o $@ -c $(filter-out $(MOZ_OPTIMIZE_FLAGS), $(COMPILE_CFLAGS)) $< jsdtoa.o: jsdtoa.c Makefile.in $(REPORT_BUILD) @$(MAKE_DEPS_AUTO) $(CC) -o $@ -c $(filter-out $(MOZ_OPTIMIZE_FLAGS), $(COMPILE_CFLAGS)) $< endif jsopcode.h jsopcode.c: jsopcode.tbl ifeq (,$(CROSS_COMPILE)$(filter-out WINNT,$(OS_ARCH))) jsautocfg.h: touch $@ else ifeq ($(OS_ARCH),WINCE) jsautocfg.h: touch $@ else jsautocfg.h: jscpucfg$(HOST_BIN_SUFFIX) @rm -f $@ jsautocfg.tmp ./jscpucfg > jsautocfg.tmp mv jsautocfg.tmp $@ endif endif # jscpucfg is a strange target # Needs to be built with the host compiler but needs to include # the mdcpucfg for the target so it needs the appropriate target defines ifdef HOST_NSPR_MDCPUCFG HOST_CC := $(HOST_CC) -DMDCPUCFG=$(TARGET_NSPR_MDCPUCFG) HOST_CFLAGS := $(patsubst -DXP_%,,$(HOST_CFLAGS)) endif ifdef CROSS_COMPILE # jscpucfg needs to know when it's supposed to produce a config for the target JSCPUCFG_DEFINES = $(ACDEFINES) # This is incredibly hacky. Darwin NSPR uses the same MDCPUCFG for multiple # processors, and determines which processor to configure for based on # #ifdef i386. This macro is among the NSPR defines, but is also automatically # defined by the compiler when building for i386. It therefore needs to be # defined here if targeting i386, and explicitly undefined otherwise. ifeq ($(OS_ARCH),Darwin) ifeq ($(TARGET_CPU),powerpc) JSCPUCFG_DEFINES += -Ui386 else JSCPUCFG_DEFINES += -Di386=1 endif endif endif ifeq ($(OS_ARCH),QNX) ifneq ($(OS_TARGET),NTO) # QNX's compiler apparently can't build a binary directly from a source file. jscpucfg.o: jscpucfg.c Makefile.in $(HOST_CC) $(HOST_CFLAGS) -c $(JSCPUCFG_DEFINES) $(DEFINES) $(NSPR_CFLAGS) -o $@ $< jscpucfg: jscpucfg.o $(HOST_CC) $(HOST_CFLAGS) $(JSCPUCFG_DEFINES) $(DEFINES) -o $@ $< endif else ifeq ($(OS_ARCH),WINCE) jscpucfg$(HOST_BIN_SUFFIX): echo no need to build jscpucfg $< else jscpucfg$(HOST_BIN_SUFFIX): jscpucfg.c Makefile.in $(HOST_CC) $(HOST_CFLAGS) $(JSCPUCFG_DEFINES) $(DEFINES) $(NSPR_CFLAGS) $(OUTOPTION)$@ $< endif endif # Extra dependancies and rules for keyword switch code jsscan.$(OBJ_SUFFIX): jsautokw.h jskeyword.tbl host_jskwgen.$(OBJ_SUFFIX): jsconfig.h jskeyword.tbl jsautokw.h: host_jskwgen$(HOST_BIN_SUFFIX) ./host_jskwgen$(HOST_BIN_SUFFIX) $@ pacparser-1.4.5/src/spidermonkey/js/src/Makefile.ref000066400000000000000000000176771464010763600224500ustar00rootroot00000000000000# -*- Mode: makefile -*- # vim: ft=make # # ***** BEGIN LICENSE BLOCK ***** # Version: MPL 1.1/GPL 2.0/LGPL 2.1 # # The contents of this file are subject to the Mozilla Public License Version # 1.1 (the "License"); you may not use this file except in compliance with # the License. You may obtain a copy of the License at # http://www.mozilla.org/MPL/ # # Software distributed under the License is distributed on an "AS IS" basis, # WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License # for the specific language governing rights and limitations under the # License. # # The Original Code is Mozilla Communicator client code, released # March 31, 1998. # # The Initial Developer of the Original Code is # Netscape Communications Corporation. # Portions created by the Initial Developer are Copyright (C) 1998 # the Initial Developer. All Rights Reserved. # # Contributor(s): # Michael Ang # Kevin Buhr # # Alternatively, the contents of this file may be used under the terms of # either of the GNU General Public License Version 2 or later (the "GPL"), # or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), # in which case the provisions of the GPL or the LGPL are applicable instead # of those above. If you wish to allow use of your version of this file only # under the terms of either the GPL or the LGPL, and not to allow others to # use your version of this file under the terms of the MPL, indicate your # decision by deleting the provisions above and replace them with the notice # and other provisions required by the GPL or the LGPL. If you do not delete # the provisions above, a recipient may use your version of this file under # the terms of any one of the MPL, the GPL or the LGPL. # # ***** END LICENSE BLOCK ***** # # JSRef GNUmake makefile. # # Note: dependency rules are missing for some files (some # .h, all .msg, etc.) Re-make clean if in doubt. # DEPTH = . include config.mk #NS_USE_NATIVE = 1 ifdef NARCISSUS DEFINES += -DNARCISSUS endif # Look in OBJDIR to find jsautocfg.h and jsautokw.h INCLUDES += -I$(OBJDIR) ifdef JS_THREADSAFE DEFINES += -DJS_THREADSAFE INCLUDES += -I$(DIST)/include/nspr ifdef USE_MSVC OTHER_LIBS += $(DIST)/lib/libnspr$(NSPR_LIBSUFFIX).lib else OTHER_LIBS += -L$(DIST)/lib -lnspr$(NSPR_LIBSUFFIX) endif endif ifdef JS_NO_THIN_LOCKS DEFINES += -DJS_USE_ONLY_NSPR_LOCKS endif ifdef JS_HAS_FILE_OBJECT DEFINES += -DJS_HAS_FILE_OBJECT endif # # XCFLAGS may be set in the environment or on the gmake command line # CFLAGS += $(OPTIMIZER) $(OS_CFLAGS) $(DEFINES) $(INCLUDES) $(XCFLAGS) LDFLAGS = $(XLDFLAGS) ifndef NO_LIBM LDFLAGS += -lm endif # Prevent floating point errors caused by VC++ optimizations ifeq ($(OS_ARCH),WINNT) _MSC_VER = $(shell $(CC) 2>&1 | sed -n 's/.*Compiler Version \([0-9]*\)\.\([0-9]*\).*/\1\2/p') ifeq (,$(filter-out 1200 1300 1310,$(_MSC_VER))) CFLAGS += -Op else CFLAGS += -fp:precise endif endif # WINNT # # Ask perl what flags it was built with, so we can build js with similar flags # and link properly. Viva gmake. # ifdef JS_PERLCONNECT DEFINES += -DPERLCONNECT -D_GNU_SOURCE PERLCFLAGS := $(shell perl -MExtUtils::Embed -e ccopts) PERLLDFLAGS := $(shell perl -MExtUtils::Embed -e ldopts) # perl erroneously reports compiler flag -rdynamic (interpreted by ld # as -r) when it really meant -export-dynamic. PERLLDFLAGS := $(subst -rdynamic,-export-dynamic,$(PERLLDFLAGS)) CFLAGS += $(PERLCFLAGS) #LDFLAGS += $(PERLLDFLAGS) #PH removed this assgnment INCLUDES += -I. #needed for perlconnect/jsperl.c endif # # Server-related changes : # ifdef NES40 DEFINES += -DNES40 endif # # Line editing support. # Define JS_READLINE or JS_EDITLINE to enable line editing in the # js command-line interpreter. # ifdef JS_READLINE # For those platforms with the readline library installed. DEFINES += -DEDITLINE PROG_LIBS += -lreadline -ltermcap else ifdef JS_EDITLINE # Use the editline library, built locally. PREDIRS += editline DEFINES += -DEDITLINE PROG_LIBS += editline/$(OBJDIR)/libedit.a endif endif # For purify PURE_CFLAGS = -DXP_UNIX $(OPTIMIZER) $(PURE_OS_CFLAGS) $(DEFINES) \ $(INCLUDES) $(XCFLAGS) # # JS file lists # JS_HFILES = \ jsarray.h \ jsatom.h \ jsbool.h \ jsconfig.h \ jscntxt.h \ jsdate.h \ jsemit.h \ jsexn.h \ jsfun.h \ jsgc.h \ jsinterp.h \ jsiter.h \ jslibmath.h \ jslock.h \ jsmath.h \ jsnum.h \ jsobj.h \ jsopcode.h \ jsparse.h \ jsarena.h \ jsclist.h \ jsdhash.h \ jsdtoa.h \ jshash.h \ jslong.h \ jsosdep.h \ jstypes.h \ jsprvtd.h \ jspubtd.h \ jsregexp.h \ jsscan.h \ jsscope.h \ jsscript.h \ jsstr.h \ jsxdrapi.h \ jsxml.h \ $(NULL) API_HFILES = \ jsapi.h \ jsdbgapi.h \ $(NULL) OTHER_HFILES = \ jsbit.h \ jscompat.h \ jscpucfg.h \ jsotypes.h \ jsstddef.h \ prmjtime.h \ resource.h \ jsopcode.tbl \ jsproto.tbl \ js.msg \ jsshell.msg \ jskeyword.tbl \ $(NULL) ifndef PREBUILT_CPUCFG OTHER_HFILES += $(OBJDIR)/jsautocfg.h endif OTHER_HFILES += $(OBJDIR)/jsautokw.h HFILES = $(JS_HFILES) $(API_HFILES) $(OTHER_HFILES) JS_CFILES = \ jsapi.c \ jsarena.c \ jsarray.c \ jsatom.c \ jsbool.c \ jscntxt.c \ jsdate.c \ jsdbgapi.c \ jsdhash.c \ jsdtoa.c \ jsemit.c \ jsexn.c \ jsfun.c \ jsgc.c \ jshash.c \ jsinterp.c \ jsiter.c \ jslock.c \ jslog2.c \ jslong.c \ jsmath.c \ jsnum.c \ jsobj.c \ jsopcode.c \ jsparse.c \ jsprf.c \ jsregexp.c \ jsscan.c \ jsscope.c \ jsscript.c \ jsstr.c \ jsutil.c \ jsxdrapi.c \ jsxml.c \ prmjtime.c \ $(NULL) ifdef JS_LIVECONNECT DIRS += liveconnect endif ifdef JS_PERLCONNECT JS_CFILES += perlconnect/jsperl.c endif ifdef JS_HAS_FILE_OBJECT JS_CFILES += jsfile.c JS_HFILES += jsfile.h endif LIB_CFILES = $(JS_CFILES) LIB_ASFILES := $(wildcard *_$(OS_ARCH).s) PROG_CFILES = js.c ifdef USE_MSVC LIBRARY = $(OBJDIR)/js32.lib SHARED_LIBRARY = $(OBJDIR)/js32.dll PROGRAM = $(OBJDIR)/js.exe else LIBRARY = $(OBJDIR)/libjs.a SHARED_LIBRARY = $(OBJDIR)/libjs.$(SO_SUFFIX) PROGRAM = $(OBJDIR)/js ifdef JS_PERLCONNECT PROG_LIBS += $(PERLLDFLAGS) endif endif include rules.mk MOZ_DEPTH = ../.. include jsconfig.mk nsinstall-target: cd ../../config; $(MAKE) OBJDIR=$(OBJDIR) OBJDIR_NAME=$(OBJDIR) # # Rules for keyword switch generation # GARBAGE += $(OBJDIR)/jsautokw.h $(OBJDIR)/jskwgen$(HOST_BIN_SUFFIX) GARBAGE += $(OBJDIR)/jskwgen.$(OBJ_SUFFIX) $(OBJDIR)/jsscan.$(OBJ_SUFFIX): $(OBJDIR)/jsautokw.h jskeyword.tbl $(OBJDIR)/jskwgen.$(OBJ_SUFFIX): jskwgen.c jskeyword.tbl $(OBJDIR)/jsautokw.h: $(OBJDIR)/jskwgen$(HOST_BIN_SUFFIX) jskeyword.tbl $(OBJDIR)/jskwgen$(HOST_BIN_SUFFIX) $@ ifdef USE_MSVC $(OBJDIR)/jskwgen.obj: jskwgen.c jskeyword.tbl @$(MAKE_OBJDIR) $(CC) -Fo$(OBJDIR)/ -c $(CFLAGS) $< $(OBJDIR)/jskwgen$(HOST_BIN_SUFFIX): $(OBJDIR)/jskwgen.$(OBJ_SUFFIX) link.exe -out:"$@" $(EXE_LINK_FLAGS) $^ else $(OBJDIR)/jskwgen.o: jskwgen.c jskeyword.tbl @$(MAKE_OBJDIR) $(CC) -o $@ -c $(CFLAGS) $< $(OBJDIR)/jskwgen$(HOST_BIN_SUFFIX): $(OBJDIR)/jskwgen.$(OBJ_SUFFIX) $(CC) -o $@ $(CFLAGS) $(LDFLAGS) $^ endif # # JS shell executable # ifdef USE_MSVC $(PROGRAM): $(PROG_OBJS) $(LIBRARY) link.exe -out:"$@" $(EXE_LINK_FLAGS) $^ else $(PROGRAM): $(PROG_OBJS) $(LIBRARY) $(CC) -o $@ $(CFLAGS) $(PROG_OBJS) $(LIBRARY) $(LDFLAGS) $(OTHER_LIBS) \ $(PROG_LIBS) endif $(PROGRAM).pure: $(PROG_OBJS) $(LIBRARY) purify $(PUREFLAGS) \ $(CC) -o $@ $(PURE_OS_CFLAGS) $(PROG_OBJS) $(LIBRARY) $(LDFLAGS) \ $(OTHER_LIBS) $(PROG_LIBS) ifndef PREBUILT_CPUCFG $(HFILES) $(CFILES): $(OBJDIR)/jsautocfg.h $(OBJDIR)/jsautocfg.h: $(OBJDIR)/jscpucfg rm -f $@ $(OBJDIR)/jscpucfg > $@ $(OBJDIR)/jscpucfg: $(OBJDIR)/jscpucfg.o $(CC) -o $@ $(OBJDIR)/jscpucfg.o # Add to TARGETS for clobber rule TARGETS += $(OBJDIR)/jsautocfg.h $(OBJDIR)/jscpucfg \ $(OBJDIR)/jscpucfg.o endif # # Hardwire dependencies on jsopcode.tbl # jsopcode.h jsopcode.c: jsopcode.tbl -include $(DEPENDENCIES) TARNAME = jsref.tar TARFILES = files `cat files` SUFFIXES: .i %.i: %.c $(CC) -C -E $(CFLAGS) $< > $*.i pacparser-1.4.5/src/spidermonkey/js/src/README.html000066400000000000000000001225541464010763600220470ustar00rootroot00000000000000 JavaScript Reference Implementation (JSRef) README

Table of Contents

Introduction

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

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

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

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

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

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

Windows

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

Macintosh

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

Unix

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

Debugging notes

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

    Naming and coding conventions

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

    Using the JS API

    Starting up

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

    Defining objects and properties

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

    Defining functions

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

    Defining classes

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

    Running scripts

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

    Calling functions

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

    Shutting down

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

    Debugging API

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

    Design walk-through

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

    JS "JavaScript Proper"

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

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

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

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

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

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

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

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

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

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

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

    File walk-through (OUT OF DATE!)

    jsapi.c, jsapi.h

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

    jspubtd.h, jsprvtd.h

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

    jsdbgapi.c, jsdbgapi.h

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

    jsconfig.h

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

    js.c

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

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

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

    jsobj.*, jsscope.*

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

    jsatom.c, jsatom.h

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

    jsgc.c, jsgc.h

    [TBD]

    jsinterp.*, jscntxt.*

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

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

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

    jstypes.h, jslog2.c

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

    jsarena.c, jsarena.h

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

    jsutil.c, jsutil.h

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

    jsclist.h

    Doubly-linked circular list struct and macros.

    jscpucfg.c

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

    prdtoa.c, prdtoa.h

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

    prhash.c, prhash.h

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

    prlong.c, prlong.h

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

    jsosdep.h

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

    jsprf.*

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

    prmjtime.c, prmjtime.h

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

    Additional Resources (links, API docs, and newsgroups)

    pacparser-1.4.5/src/spidermonkey/js/src/SpiderMonkey.rsp000066400000000000000000000006421464010763600233540ustar00rootroot00000000000000mozilla/js/src/* mozilla/js/src/config/* mozilla/js/src/fdlibm/* mozilla/js/src/liveconnect/* mozilla/js/src/liveconnect/_jni/* mozilla/js/src/liveconnect/classes/* mozilla/js/src/liveconnect/classes/netscape/* mozilla/js/src/liveconnect/classes/netscape/javascript/* mozilla/js/src/liveconnect/config/* mozilla/js/src/liveconnect/macbuild/* mozilla/js/src/liveconnect/macbuild/JavaSession/* mozilla/js/src/macbuild/* pacparser-1.4.5/src/spidermonkey/js/src/Y.js000066400000000000000000000006001464010763600207550ustar00rootroot00000000000000// The Y combinator, applied to the factorial function function factorial(proc) { return function (n) { return (n <= 1) ? 1 : n * proc(n-1); } } function Y(outer) { function inner(proc) { function apply(arg) { return proc(proc)(arg); } return outer(apply); } return inner(inner); } print("5! is " + Y(factorial)(5)); pacparser-1.4.5/src/spidermonkey/js/src/config.mk000066400000000000000000000114131464010763600220110ustar00rootroot00000000000000# -*- Mode: makefile -*- # # ***** BEGIN LICENSE BLOCK ***** # Version: MPL 1.1/GPL 2.0/LGPL 2.1 # # The contents of this file are subject to the Mozilla Public License Version # 1.1 (the "License"); you may not use this file except in compliance with # the License. You may obtain a copy of the License at # http://www.mozilla.org/MPL/ # # Software distributed under the License is distributed on an "AS IS" basis, # WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License # for the specific language governing rights and limitations under the # License. # # The Original Code is Mozilla Communicator client code, released # March 31, 1998. # # The Initial Developer of the Original Code is # Netscape Communications Corporation. # Portions created by the Initial Developer are Copyright (C) 1998-1999 # the Initial Developer. All Rights Reserved. # # Contributor(s): # # Alternatively, the contents of this file may be used under the terms of # either of the GNU General Public License Version 2 or later (the "GPL"), # or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), # in which case the provisions of the GPL or the LGPL are applicable instead # of those above. If you wish to allow use of your version of this file only # under the terms of either the GPL or the LGPL, and not to allow others to # use your version of this file under the terms of the MPL, indicate your # decision by deleting the provisions above and replace them with the notice # and other provisions required by the GPL or the LGPL. If you do not delete # the provisions above, a recipient may use your version of this file under # the terms of any one of the MPL, the GPL or the LGPL. # # ***** END LICENSE BLOCK ***** ifdef JS_DIST DIST = $(JS_DIST) else DIST = $(DEPTH)/../../dist endif # Set os+release dependent make variables OS_ARCH := $(subst /,_,$(shell uname -s | sed /\ /s//_/)) # Attempt to differentiate between SunOS 5.4 and x86 5.4 OS_CPUARCH := $(shell uname -m) ifeq ($(OS_CPUARCH),i86pc) OS_RELEASE := $(shell uname -r)_$(OS_CPUARCH) else ifeq ($(OS_ARCH),AIX) OS_RELEASE := $(shell uname -v).$(shell uname -r) else OS_RELEASE := $(shell uname -r) endif endif ifeq ($(OS_ARCH),IRIX64) OS_ARCH := IRIX endif # Handle output from win32 unames other than Netscape's version ifeq (,$(filter-out Windows_95 Windows_98 CYGWIN_95-4.0 CYGWIN_98-4.10, $(OS_ARCH))) OS_ARCH := WIN95 endif ifeq ($(OS_ARCH),WIN95) OS_ARCH := WINNT OS_RELEASE := 4.0 endif ifeq ($(OS_ARCH), Windows_NT) OS_ARCH := WINNT OS_MINOR_RELEASE := $(shell uname -v) ifeq ($(OS_MINOR_RELEASE),00) OS_MINOR_RELEASE = 0 endif OS_RELEASE := $(OS_RELEASE).$(OS_MINOR_RELEASE) endif ifeq (CYGWIN_NT,$(findstring CYGWIN_NT,$(OS_ARCH))) OS_RELEASE := $(patsubst CYGWIN_NT-%,%,$(OS_ARCH)) OS_ARCH := WINNT endif ifeq ($(OS_ARCH), CYGWIN32_NT) OS_ARCH := WINNT endif ifeq (MINGW32_NT,$(findstring MINGW32_NT,$(OS_ARCH))) OS_RELEASE := $(patsubst MINGW32_NT-%,%,$(OS_ARCH)) OS_ARCH := WINNT endif # Virtually all Linux versions are identical. # Any distinctions are handled in linux.h ifeq ($(OS_ARCH),Linux) OS_CONFIG := Linux_All else ifeq ($(OS_ARCH),dgux) OS_CONFIG := dgux else ifeq ($(OS_ARCH),Darwin) OS_CONFIG := Darwin else ifeq ($(OS_ARCH),FreeBSD) # Add this line for FreeBSD OS_CONFIG := FreeBSD else OS_CONFIG := $(OS_ARCH)$(OS_OBJTYPE)$(OS_RELEASE) endif ASFLAGS = DEFINES = ifeq ($(OS_ARCH), WINNT) INSTALL = nsinstall CP = cp else INSTALL = $(DIST)/bin/nsinstall CP = cp endif ifdef BUILD_OPT OPTIMIZER = -O DEFINES += -UDEBUG -DNDEBUG -UDEBUG_$(USER) OBJDIR_TAG = _OPT else ifdef USE_MSVC OPTIMIZER = -Zi else OPTIMIZER = -g endif DEFINES += -DDEBUG -DDEBUG_$(USER) OBJDIR_TAG = _DBG endif SO_SUFFIX = so NS_USE_NATIVE = 1 # Java stuff CLASSDIR = $(DEPTH)/liveconnect/classes JAVA_CLASSES = $(patsubst %.java,%.class,$(JAVA_SRCS)) TARGETS += $(addprefix $(CLASSDIR)/$(OBJDIR)/$(JARPATH)/, $(JAVA_CLASSES)) JAVAC = $(JDK)/bin/javac JAVAC_FLAGS = -classpath "$(CLASSPATH)" -d $(CLASSDIR)/$(OBJDIR) ifeq ($(OS_ARCH), WINNT) SEP = ; else SEP = : endif CLASSPATH = $(JDK)/lib/classes.zip$(SEP)$(CLASSDIR)/$(OBJDIR) include $(DEPTH)/config/$(OS_CONFIG).mk ifndef OBJ_SUFFIX ifdef USE_MSVC OBJ_SUFFIX = obj else OBJ_SUFFIX = o endif endif ifndef HOST_BIN_SUFFIX ifeq ($(OS_ARCH),WINNT) HOST_BIN_SUFFIX = .exe else HOST_BIN_SUFFIX = endif endif # Name of the binary code directories ifdef BUILD_IDG OBJDIR = $(OS_CONFIG)$(OBJDIR_TAG).OBJD else OBJDIR = $(OS_CONFIG)$(OBJDIR_TAG).OBJ endif VPATH = $(OBJDIR) # Automatic make dependencies file DEPENDENCIES = $(OBJDIR)/.md LCJAR = js15lc30.jar # Library name LIBDIR := lib ifeq ($(CPU_ARCH), x86_64) LIBDIR := lib64 endif pacparser-1.4.5/src/spidermonkey/js/src/config/000077500000000000000000000000001464010763600214605ustar00rootroot00000000000000pacparser-1.4.5/src/spidermonkey/js/src/config/AIX4.1.mk000066400000000000000000000043021464010763600226540ustar00rootroot00000000000000# -*- Mode: makefile -*- # # ***** BEGIN LICENSE BLOCK ***** # Version: MPL 1.1/GPL 2.0/LGPL 2.1 # # The contents of this file are subject to the Mozilla Public License Version # 1.1 (the "License"); you may not use this file except in compliance with # the License. You may obtain a copy of the License at # http://www.mozilla.org/MPL/ # # Software distributed under the License is distributed on an "AS IS" basis, # WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License # for the specific language governing rights and limitations under the # License. # # The Original Code is Mozilla Communicator client code, released # March 31, 1998. # # The Initial Developer of the Original Code is # Netscape Communications Corporation. # Portions created by the Initial Developer are Copyright (C) 1998 # the Initial Developer. All Rights Reserved. # # Contributor(s): # # Alternatively, the contents of this file may be used under the terms of # either the GNU General Public License Version 2 or later (the "GPL"), or # the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), # in which case the provisions of the GPL or the LGPL are applicable instead # of those above. If you wish to allow use of your version of this file only # under the terms of either the GPL or the LGPL, and not to allow others to # use your version of this file under the terms of the MPL, indicate your # decision by deleting the provisions above and replace them with the notice # and other provisions required by the GPL or the LGPL. If you do not delete # the provisions above, a recipient may use your version of this file under # the terms of any one of the MPL, the GPL or the LGPL. # # ***** END LICENSE BLOCK ***** # # Config stuff for AIX # CC = xlC_r CCC = xlC_r RANLIB = ranlib #.c.o: # $(CC) -c -MD $*.d $(CFLAGS) $< ARCH := aix CPU_ARCH = rs6000 GFX_ARCH = x INLINES = js_compare_and_swap:js_fast_lock1:js_fast_unlock1:js_lock_get_slot:js_lock_set_slot:js_lock_scope1 OS_CFLAGS = -qarch=com -qinline+$(INLINES) -DXP_UNIX -DAIX -DAIXV3 -DSYSV -DHAVE_LOCALTIME_R OS_LIBS = -lbsd -lsvld -lm #-lpthreads -lc_r MKSHLIB = $(LD) -bM:SRE -bh:4 -bnoentry -berok XLDFLAGS += -lc ifdef JS_THREADSAFE XLDFLAGS += -lsvld endif pacparser-1.4.5/src/spidermonkey/js/src/config/AIX4.2.mk000066400000000000000000000042601464010763600226600ustar00rootroot00000000000000# -*- Mode: makefile -*- # # ***** BEGIN LICENSE BLOCK ***** # Version: MPL 1.1/GPL 2.0/LGPL 2.1 # # The contents of this file are subject to the Mozilla Public License Version # 1.1 (the "License"); you may not use this file except in compliance with # the License. You may obtain a copy of the License at # http://www.mozilla.org/MPL/ # # Software distributed under the License is distributed on an "AS IS" basis, # WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License # for the specific language governing rights and limitations under the # License. # # The Original Code is Mozilla Communicator client code, released # March 31, 1998. # # The Initial Developer of the Original Code is # Netscape Communications Corporation. # Portions created by the Initial Developer are Copyright (C) 1998 # the Initial Developer. All Rights Reserved. # # Contributor(s): # # Alternatively, the contents of this file may be used under the terms of # either the GNU General Public License Version 2 or later (the "GPL"), or # the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), # in which case the provisions of the GPL or the LGPL are applicable instead # of those above. If you wish to allow use of your version of this file only # under the terms of either the GPL or the LGPL, and not to allow others to # use your version of this file under the terms of the MPL, indicate your # decision by deleting the provisions above and replace them with the notice # and other provisions required by the GPL or the LGPL. If you do not delete # the provisions above, a recipient may use your version of this file under # the terms of any one of the MPL, the GPL or the LGPL. # # ***** END LICENSE BLOCK ***** # # Config stuff for AIX # CC = xlC_r CCC = xlC_r CFLAGS += -qarch=com -qnoansialias -qinline+$(INLINES) -DXP_UNIX -DAIX -DAIXV3 -DSYSV -DHAVE_LOCALTIME_R RANLIB = ranlib #.c.o: # $(CC) -c -MD $*.d $(CFLAGS) $< ARCH := aix CPU_ARCH = rs6000 GFX_ARCH = x INLINES = js_compare_and_swap:js_fast_lock1:js_fast_unlock1:js_lock_get_slot:js_lock_set_slot:js_lock_scope1 #-lpthreads -lc_r MKSHLIB = /usr/lpp/xlC/bin/makeC++SharedLib_r -p 0 -G -berok ifdef JS_THREADSAFE XLDFLAGS += -ldl endif pacparser-1.4.5/src/spidermonkey/js/src/config/AIX4.3.mk000066400000000000000000000043341464010763600226630ustar00rootroot00000000000000# -*- Mode: makefile -*- # # ***** BEGIN LICENSE BLOCK ***** # Version: MPL 1.1/GPL 2.0/LGPL 2.1 # # The contents of this file are subject to the Mozilla Public License Version # 1.1 (the "License"); you may not use this file except in compliance with # the License. You may obtain a copy of the License at # http://www.mozilla.org/MPL/ # # Software distributed under the License is distributed on an "AS IS" basis, # WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License # for the specific language governing rights and limitations under the # License. # # The Original Code is Mozilla Communicator client code, released # March 31, 1998. # # The Initial Developer of the Original Code is # Netscape Communications Corporation. # Portions created by the Initial Developer are Copyright (C) 1998 # the Initial Developer. All Rights Reserved. # # Contributor(s): # # Alternatively, the contents of this file may be used under the terms of # either the GNU General Public License Version 2 or later (the "GPL"), or # the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), # in which case the provisions of the GPL or the LGPL are applicable instead # of those above. If you wish to allow use of your version of this file only # under the terms of either the GPL or the LGPL, and not to allow others to # use your version of this file under the terms of the MPL, indicate your # decision by deleting the provisions above and replace them with the notice # and other provisions required by the GPL or the LGPL. If you do not delete # the provisions above, a recipient may use your version of this file under # the terms of any one of the MPL, the GPL or the LGPL. # # ***** END LICENSE BLOCK ***** # # Config stuff for AIX # CC = xlC_r CCC = xlC_r CFLAGS += -qarch=com -qnoansialias -qinline+$(INLINES) -DXP_UNIX -DAIX -DAIXV3 -DSYSV -DAIX4_3 -DHAVE_LOCALTIME_R RANLIB = ranlib #.c.o: # $(CC) -c -MD $*.d $(CFLAGS) $< ARCH := aix CPU_ARCH = rs6000 GFX_ARCH = x INLINES = js_compare_and_swap:js_fast_lock1:js_fast_unlock1:js_lock_get_slot:js_lock_set_slot:js_lock_scope1 #-lpthreads -lc_r MKSHLIB_BIN = /usr/ibmcxx/bin/makeC++SharedLib_r MKSHLIB = $(MKSHLIB_BIN) -p 0 -G -berok -bM:UR ifdef JS_THREADSAFE XLDFLAGS += -ldl endif pacparser-1.4.5/src/spidermonkey/js/src/config/CVS/000077500000000000000000000000001464010763600221135ustar00rootroot00000000000000pacparser-1.4.5/src/spidermonkey/js/src/config/CVS/Entries000066400000000000000000000034021464010763600234460ustar00rootroot00000000000000/AIX4.1.mk/1.7/Sat Feb 12 20:10:33 2005//TJS_170 /AIX4.2.mk/1.9/Sat Feb 12 20:10:33 2005//TJS_170 /AIX4.3.mk/1.9/Sat Feb 12 20:10:33 2005//TJS_170 /Darwin.mk/1.6/Mon Feb 5 16:24:49 2007//TJS_170 /Darwin1.3.mk/1.3/Sat Feb 12 20:10:33 2005//TJS_170 /Darwin1.4.mk/1.3/Sat Feb 12 20:10:33 2005//TJS_170 /Darwin5.2.mk/1.3/Sat Feb 12 20:10:33 2005//TJS_170 /Darwin5.3.mk/1.3/Sat Feb 12 20:10:33 2005//TJS_170 /HP-UXB.10.10.mk/1.9/Sat Feb 12 20:10:33 2005//TJS_170 /HP-UXB.10.20.mk/1.8/Sat Feb 12 20:10:33 2005//TJS_170 /HP-UXB.11.00.mk/1.9/Sat Feb 12 20:10:33 2005//TJS_170 /IRIX.mk/1.9/Sat Feb 12 20:10:33 2005//TJS_170 /IRIX5.3.mk/1.7/Sat Feb 12 20:10:33 2005//TJS_170 /IRIX6.1.mk/1.7/Sat Feb 12 20:10:33 2005//TJS_170 /IRIX6.2.mk/1.6/Sat Feb 12 20:10:33 2005//TJS_170 /IRIX6.3.mk/1.6/Sat Feb 12 20:10:33 2005//TJS_170 /IRIX6.5.mk/1.6/Sat Feb 12 20:10:33 2005//TJS_170 /Linux_All.mk/1.14/Tue May 10 19:53:44 2005//TJS_170 /Mac_OS10.0.mk/1.4/Sat Feb 12 20:10:33 2005//TJS_170 /OSF1V4.0.mk/1.9/Sat Feb 12 20:10:33 2005//TJS_170 /OSF1V5.0.mk/1.5/Sat Feb 12 20:10:33 2005//TJS_170 /SunOS4.1.4.mk/1.6/Sat Feb 12 20:10:33 2005//TJS_170 /SunOS5.3.mk/1.7/Sat Feb 12 20:10:33 2005//TJS_170 /SunOS5.4.mk/1.7/Sat Feb 12 20:10:33 2005//TJS_170 /SunOS5.5.1.mk/1.8/Sat Feb 12 20:10:33 2005//TJS_170 /SunOS5.5.mk/1.10/Sat Feb 12 20:10:33 2005//TJS_170 /SunOS5.6.mk/1.13/Sat Feb 12 20:10:33 2005//TJS_170 /SunOS5.7.mk/1.6/Sat Feb 12 20:10:33 2005//TJS_170 /SunOS5.8.mk/1.4/Sat Feb 12 20:10:33 2005//TJS_170 /SunOS5.9.mk/1.2/Sat Feb 12 20:10:33 2005//TJS_170 /WINNT4.0.mk/1.15/Wed Jul 18 19:55:15 2007//TJS_170 /WINNT5.0.mk/1.10/Fri Aug 10 23:23:38 2007//TJS_170 /WINNT5.1.mk/1.6/Fri Aug 10 23:23:38 2007//TJS_170 /WINNT5.2.mk/1.5/Fri Aug 10 23:23:38 2007//TJS_170 /dgux.mk/1.7/Sat Feb 12 20:10:33 2005//TJS_170 D pacparser-1.4.5/src/spidermonkey/js/src/config/CVS/Repository000066400000000000000000000000261464010763600242130ustar00rootroot00000000000000mozilla/js/src/config pacparser-1.4.5/src/spidermonkey/js/src/config/CVS/Root000066400000000000000000000000631464010763600227600ustar00rootroot00000000000000:pserver:anonymous@cvs-mirror.mozilla.org:/cvsroot pacparser-1.4.5/src/spidermonkey/js/src/config/CVS/Tag000066400000000000000000000000101464010763600225400ustar00rootroot00000000000000NJS_170 pacparser-1.4.5/src/spidermonkey/js/src/config/Darwin.mk000066400000000000000000000050601464010763600232360ustar00rootroot00000000000000# -*- Mode: makefile -*- # # ***** BEGIN LICENSE BLOCK ***** # Version: MPL 1.1/GPL 2.0/LGPL 2.1 # # The contents of this file are subject to the Mozilla Public License Version # 1.1 (the "License"); you may not use this file except in compliance with # the License. You may obtain a copy of the License at # http://www.mozilla.org/MPL/ # # Software distributed under the License is distributed on an "AS IS" basis, # WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License # for the specific language governing rights and limitations under the # License. # # The Original Code is Mozilla Communicator client code, released # March 31, 1998. # # The Initial Developer of the Original Code is # Netscape Communications Corporation. # Portions created by the Initial Developer are Copyright (C) 1998 # the Initial Developer. All Rights Reserved. # # Contributor(s): # Steve Zellers (zellers@apple.com) # # Alternatively, the contents of this file may be used under the terms of # either the GNU General Public License Version 2 or later (the "GPL"), or # the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), # in which case the provisions of the GPL or the LGPL are applicable instead # of those above. If you wish to allow use of your version of this file only # under the terms of either the GPL or the LGPL, and not to allow others to # use your version of this file under the terms of the MPL, indicate your # decision by deleting the provisions above and replace them with the notice # and other provisions required by the GPL or the LGPL. If you do not delete # the provisions above, a recipient may use your version of this file under # the terms of any one of the MPL, the GPL or the LGPL. # # ***** END LICENSE BLOCK ***** # # Config for Mac OS X as of PR3 # Just ripped from Linux config # CC = cc CCC = g++ CFLAGS += -Wall -Wno-format OS_CFLAGS = -DXP_UNIX -DSVR4 -DSYSV -D_BSD_SOURCE -DPOSIX_SOURCE -DDARWIN RANLIB = ranlib MKSHLIB = $(CC) -dynamiclib $(XMKSHLIBOPTS) -framework System SO_SUFFIX = dylib #.c.o: # $(CC) -c -MD $*.d $(CFLAGS) $< CPU_ARCH = $(shell uname -m) ifeq (86,$(findstring 86,$(CPU_ARCH))) CPU_ARCH = x86 OS_CFLAGS+= -DX86_LINUX endif GFX_ARCH = x OS_LIBS = -lc -framework System ASFLAGS += -x assembler-with-cpp ifeq ($(CPU_ARCH),alpha) # Ask the C compiler on alpha linux to let us work with denormalized # double values, which are required by the ECMA spec. OS_CFLAGS += -mieee endif # Use the editline library to provide line-editing support. JS_EDITLINE = 1 # Don't allow Makefile.ref to use libmath NO_LIBM = 1 pacparser-1.4.5/src/spidermonkey/js/src/config/Darwin1.3.mk000077500000000000000000000050251464010763600234640ustar00rootroot00000000000000# -*- Mode: makefile -*- # # ***** BEGIN LICENSE BLOCK ***** # Version: MPL 1.1/GPL 2.0/LGPL 2.1 # # The contents of this file are subject to the Mozilla Public License Version # 1.1 (the "License"); you may not use this file except in compliance with # the License. You may obtain a copy of the License at # http://www.mozilla.org/MPL/ # # Software distributed under the License is distributed on an "AS IS" basis, # WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License # for the specific language governing rights and limitations under the # License. # # The Original Code is Mozilla Communicator client code, released # March 31, 1998. # # The Initial Developer of the Original Code is # Netscape Communications Corporation. # Portions created by the Initial Developer are Copyright (C) 1998 # the Initial Developer. All Rights Reserved. # # Contributor(s): # Steve Zellers (zellers@apple.com) # # Alternatively, the contents of this file may be used under the terms of # either the GNU General Public License Version 2 or later (the "GPL"), or # the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), # in which case the provisions of the GPL or the LGPL are applicable instead # of those above. If you wish to allow use of your version of this file only # under the terms of either the GPL or the LGPL, and not to allow others to # use your version of this file under the terms of the MPL, indicate your # decision by deleting the provisions above and replace them with the notice # and other provisions required by the GPL or the LGPL. If you do not delete # the provisions above, a recipient may use your version of this file under # the terms of any one of the MPL, the GPL or the LGPL. # # ***** END LICENSE BLOCK ***** # # Config for Mac OS X as of PR3 # Just ripped from Linux config # CC = cc CCC = g++ CFLAGS += -Wall -Wno-format OS_CFLAGS = -DXP_UNIX -DSVR4 -DSYSV -D_BSD_SOURCE -DPOSIX_SOURCE -DRHAPSODY RANLIB = ranlib MKSHLIB = libtool $(XMKSHLIBOPTS) -framework System #.c.o: # $(CC) -c -MD $*.d $(CFLAGS) $< CPU_ARCH = $(shell uname -m) ifeq (86,$(findstring 86,$(CPU_ARCH))) CPU_ARCH = x86 OS_CFLAGS+= -DX86_LINUX endif GFX_ARCH = x OS_LIBS = -lc -framework System ASFLAGS += -x assembler-with-cpp ifeq ($(CPU_ARCH),alpha) # Ask the C compiler on alpha linux to let us work with denormalized # double values, which are required by the ECMA spec. OS_CFLAGS += -mieee endif # Use the editline library to provide line-editing support. JS_EDITLINE = 1 # Don't allow Makefile.ref to use libmath NO_LIBM = 1 pacparser-1.4.5/src/spidermonkey/js/src/config/Darwin1.4.mk000077500000000000000000000034211464010763600234630ustar00rootroot00000000000000# -*- Mode: makefile -*- # # ***** BEGIN LICENSE BLOCK ***** # Version: MPL 1.1/GPL 2.0/LGPL 2.1 # # The contents of this file are subject to the Mozilla Public License Version # 1.1 (the "License"); you may not use this file except in compliance with # the License. You may obtain a copy of the License at # http://www.mozilla.org/MPL/ # # Software distributed under the License is distributed on an "AS IS" basis, # WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License # for the specific language governing rights and limitations under the # License. # # The Original Code is Mozilla Communicator client code, released # March 31, 1998. # # The Initial Developer of the Original Code is # Netscape Communications Corporation. # Portions created by the Initial Developer are Copyright (C) 1998 # the Initial Developer. All Rights Reserved. # # Contributor(s): # Mike McCabe # # Alternatively, the contents of this file may be used under the terms of # either the GNU General Public License Version 2 or later (the "GPL"), or # the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), # in which case the provisions of the GPL or the LGPL are applicable instead # of those above. If you wish to allow use of your version of this file only # under the terms of either the GPL or the LGPL, and not to allow others to # use your version of this file under the terms of the MPL, indicate your # decision by deleting the provisions above and replace them with the notice # and other provisions required by the GPL or the LGPL. If you do not delete # the provisions above, a recipient may use your version of this file under # the terms of any one of the MPL, the GPL or the LGPL. # # ***** END LICENSE BLOCK ***** include $(DEPTH)/config/Darwin1.3.mk pacparser-1.4.5/src/spidermonkey/js/src/config/Darwin5.2.mk000077500000000000000000000050231464010763600234650ustar00rootroot00000000000000# -*- Mode: makefile -*- # # ***** BEGIN LICENSE BLOCK ***** # Version: MPL 1.1/GPL 2.0/LGPL 2.1 # # The contents of this file are subject to the Mozilla Public License Version # 1.1 (the "License"); you may not use this file except in compliance with # the License. You may obtain a copy of the License at # http://www.mozilla.org/MPL/ # # Software distributed under the License is distributed on an "AS IS" basis, # WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License # for the specific language governing rights and limitations under the # License. # # The Original Code is Mozilla Communicator client code, released # March 31, 1998. # # The Initial Developer of the Original Code is # Netscape Communications Corporation. # Portions created by the Initial Developer are Copyright (C) 1998 # the Initial Developer. All Rights Reserved. # # Contributor(s): # Steve Zellers (zellers@apple.com) # # Alternatively, the contents of this file may be used under the terms of # either the GNU General Public License Version 2 or later (the "GPL"), or # the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), # in which case the provisions of the GPL or the LGPL are applicable instead # of those above. If you wish to allow use of your version of this file only # under the terms of either the GPL or the LGPL, and not to allow others to # use your version of this file under the terms of the MPL, indicate your # decision by deleting the provisions above and replace them with the notice # and other provisions required by the GPL or the LGPL. If you do not delete # the provisions above, a recipient may use your version of this file under # the terms of any one of the MPL, the GPL or the LGPL. # # ***** END LICENSE BLOCK ***** # # Config for Mac OS X as of PR3 # Just ripped from Linux config # CC = cc CCC = g++ CFLAGS += -Wall -Wno-format OS_CFLAGS = -DXP_UNIX -DSVR4 -DSYSV -D_BSD_SOURCE -DPOSIX_SOURCE -DDARWIN RANLIB = ranlib MKSHLIB = libtool $(XMKSHLIBOPTS) -framework System #.c.o: # $(CC) -c -MD $*.d $(CFLAGS) $< CPU_ARCH = $(shell uname -m) ifeq (86,$(findstring 86,$(CPU_ARCH))) CPU_ARCH = x86 OS_CFLAGS+= -DX86_LINUX endif GFX_ARCH = x OS_LIBS = -lc -framework System ASFLAGS += -x assembler-with-cpp ifeq ($(CPU_ARCH),alpha) # Ask the C compiler on alpha linux to let us work with denormalized # double values, which are required by the ECMA spec. OS_CFLAGS += -mieee endif # Use the editline library to provide line-editing support. JS_EDITLINE = 1 # Don't allow Makefile.ref to use libmath NO_LIBM = 1 pacparser-1.4.5/src/spidermonkey/js/src/config/Darwin5.3.mk000066400000000000000000000050231464010763600234630ustar00rootroot00000000000000# -*- Mode: makefile -*- # # ***** BEGIN LICENSE BLOCK ***** # Version: MPL 1.1/GPL 2.0/LGPL 2.1 # # The contents of this file are subject to the Mozilla Public License Version # 1.1 (the "License"); you may not use this file except in compliance with # the License. You may obtain a copy of the License at # http://www.mozilla.org/MPL/ # # Software distributed under the License is distributed on an "AS IS" basis, # WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License # for the specific language governing rights and limitations under the # License. # # The Original Code is Mozilla Communicator client code, released # March 31, 1998. # # The Initial Developer of the Original Code is # Netscape Communications Corporation. # Portions created by the Initial Developer are Copyright (C) 1998 # the Initial Developer. All Rights Reserved. # # Contributor(s): # Steve Zellers (zellers@apple.com) # # Alternatively, the contents of this file may be used under the terms of # either the GNU General Public License Version 2 or later (the "GPL"), or # the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), # in which case the provisions of the GPL or the LGPL are applicable instead # of those above. If you wish to allow use of your version of this file only # under the terms of either the GPL or the LGPL, and not to allow others to # use your version of this file under the terms of the MPL, indicate your # decision by deleting the provisions above and replace them with the notice # and other provisions required by the GPL or the LGPL. If you do not delete # the provisions above, a recipient may use your version of this file under # the terms of any one of the MPL, the GPL or the LGPL. # # ***** END LICENSE BLOCK ***** # # Config for Mac OS X as of PR3 # Just ripped from Linux config # CC = cc CCC = g++ CFLAGS += -Wall -Wno-format OS_CFLAGS = -DXP_UNIX -DSVR4 -DSYSV -D_BSD_SOURCE -DPOSIX_SOURCE -DDARWIN RANLIB = ranlib MKSHLIB = libtool $(XMKSHLIBOPTS) -framework System #.c.o: # $(CC) -c -MD $*.d $(CFLAGS) $< CPU_ARCH = $(shell uname -m) ifeq (86,$(findstring 86,$(CPU_ARCH))) CPU_ARCH = x86 OS_CFLAGS+= -DX86_LINUX endif GFX_ARCH = x OS_LIBS = -lc -framework System ASFLAGS += -x assembler-with-cpp ifeq ($(CPU_ARCH),alpha) # Ask the C compiler on alpha linux to let us work with denormalized # double values, which are required by the ECMA spec. OS_CFLAGS += -mieee endif # Use the editline library to provide line-editing support. JS_EDITLINE = 1 # Don't allow Makefile.ref to use libmath NO_LIBM = 1 pacparser-1.4.5/src/spidermonkey/js/src/config/FreeBSD.mk000066400000000000000000000056111464010763600232260ustar00rootroot00000000000000# -*- Mode: makefile -*- # # ***** BEGIN LICENSE BLOCK ***** # Version: MPL 1.1/GPL 2.0/LGPL 2.1 # # The contents of this file are subject to the Mozilla Public License Version # 1.1 (the "License"); you may not use this file except in compliance with # the License. You may obtain a copy of the License at # http://www.mozilla.org/MPL/ # # Software distributed under the License is distributed on an "AS IS" basis, # WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License # for the specific language governing rights and limitations under the # License. # # The Original Code is Mozilla Communicator client code, released # March 31, 1998. # # The Initial Developer of the Original Code is # Netscape Communications Corporation. # Portions created by the Initial Developer are Copyright (C) 1998 # the Initial Developer. All Rights Reserved. # # Contributor(s): # # Alternatively, the contents of this file may be used under the terms of # either the GNU General Public License Version 2 or later (the "GPL"), or # the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), # in which case the provisions of the GPL or the LGPL are applicable instead # of those above. If you wish to allow use of your version of this file only # under the terms of either the GPL or the LGPL, and not to allow others to # use your version of this file under the terms of the MPL, indicate your # decision by deleting the provisions above and replace them with the notice # and other provisions required by the GPL or the LGPL. If you do not delete # the provisions above, a recipient may use your version of this file under # the terms of any one of the MPL, the GPL or the LGPL. # # ***** END LICENSE BLOCK ***** # # Config for all versions of FreeBSD # CC ?= gcc CCC ?= g++ CFLAGS += -Wall -Wno-format OS_CFLAGS = -DXP_UNIX -DSVR4 -DSYSV -D_BSD_SOURCE -DPOSIX_SOURCE -DHAVE_LOCALTIME_R RANLIB = echo MKSHLIB = $(LD) -shared $(XMKSHLIBOPTS) #.c.o: # $(CC) -c -MD $*.d $(CFLAGS) $< CPU_ARCH = $(shell uname -m) # don't filter in x86-64 architecture ifneq (amd64,$(CPU_ARCH)) ifeq (86,$(findstring 86,$(CPU_ARCH))) CPU_ARCH = x86 OS_CFLAGS+= -DX86_LINUX ifeq (gcc, $(CC)) # if using gcc on x86, check version for opt bug # (http://bugzilla.mozilla.org/show_bug.cgi?id=24892) GCC_VERSION := $(shell gcc -v 2>&1 | grep version | awk '{ print $$3 }') GCC_LIST:=$(sort 2.91.66 $(GCC_VERSION) ) ifeq (2.91.66, $(firstword $(GCC_LIST))) CFLAGS+= -DGCC_OPT_BUG endif endif endif endif GFX_ARCH = x OS_LIBS = -lm -lc ASFLAGS += -x assembler-with-cpp # Use the editline library to provide line-editing support. JS_EDITLINE = 1 ifeq ($(CPU_ARCH),amd64) # Use VA_COPY() standard macro on x86-64 # FIXME: better use it everywhere OS_CFLAGS += -DHAVE_VA_COPY -DVA_COPY=va_copy endif ifeq ($(CPU_ARCH),amd64) # We need PIC code for shared libraries # FIXME: better patch rules.mk & fdlibm/Makefile* OS_CFLAGS += -DPIC -fPIC endif pacparser-1.4.5/src/spidermonkey/js/src/config/HP-UXB.10.10.mk000066400000000000000000000044741464010763600234230ustar00rootroot00000000000000# -*- Mode: makefile -*- # # ***** BEGIN LICENSE BLOCK ***** # Version: MPL 1.1/GPL 2.0/LGPL 2.1 # # The contents of this file are subject to the Mozilla Public License Version # 1.1 (the "License"); you may not use this file except in compliance with # the License. You may obtain a copy of the License at # http://www.mozilla.org/MPL/ # # Software distributed under the License is distributed on an "AS IS" basis, # WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License # for the specific language governing rights and limitations under the # License. # # The Original Code is Mozilla Communicator client code, released # March 31, 1998. # # The Initial Developer of the Original Code is # Netscape Communications Corporation. # Portions created by the Initial Developer are Copyright (C) 1998 # the Initial Developer. All Rights Reserved. # # Contributor(s): # # Alternatively, the contents of this file may be used under the terms of # either the GNU General Public License Version 2 or later (the "GPL"), or # the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), # in which case the provisions of the GPL or the LGPL are applicable instead # of those above. If you wish to allow use of your version of this file only # under the terms of either the GPL or the LGPL, and not to allow others to # use your version of this file under the terms of the MPL, indicate your # decision by deleting the provisions above and replace them with the notice # and other provisions required by the GPL or the LGPL. If you do not delete # the provisions above, a recipient may use your version of this file under # the terms of any one of the MPL, the GPL or the LGPL. # # ***** END LICENSE BLOCK ***** # # Config stuff for HPUX # # CC = gcc # CCC = g++ # CFLAGS += -Wall -Wno-format -fPIC CC = cc -Ae +Z CCC = CC -Ae +a1 +eh +Z RANLIB = echo MKSHLIB = $(LD) -b SO_SUFFIX = sl #.c.o: # $(CC) -c -MD $*.d $(CFLAGS) $< CPU_ARCH = hppa GFX_ARCH = x OS_CFLAGS = -DXP_UNIX -DHPUX -DSYSV -DHAVE_LOCALTIME_R OS_LIBS = -ldld ifeq ($(OS_RELEASE),B.10) PLATFORM_FLAGS += -DHPUX10 -Dhpux10 PORT_FLAGS += -DRW_NO_OVERLOAD_SCHAR -DHAVE_MODEL_H ifeq ($(OS_VERSION),.10) PLATFORM_FLAGS += -DHPUX10_10 endif ifeq ($(OS_VERSION),.20) PLATFORM_FLAGS += -DHPUX10_20 endif ifeq ($(OS_VERSION),.30) PLATFORM_FLAGS += -DHPUX10_30 endif endif pacparser-1.4.5/src/spidermonkey/js/src/config/HP-UXB.10.20.mk000066400000000000000000000044741464010763600234240ustar00rootroot00000000000000# -*- Mode: makefile -*- # # ***** BEGIN LICENSE BLOCK ***** # Version: MPL 1.1/GPL 2.0/LGPL 2.1 # # The contents of this file are subject to the Mozilla Public License Version # 1.1 (the "License"); you may not use this file except in compliance with # the License. You may obtain a copy of the License at # http://www.mozilla.org/MPL/ # # Software distributed under the License is distributed on an "AS IS" basis, # WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License # for the specific language governing rights and limitations under the # License. # # The Original Code is Mozilla Communicator client code, released # March 31, 1998. # # The Initial Developer of the Original Code is # Netscape Communications Corporation. # Portions created by the Initial Developer are Copyright (C) 1998 # the Initial Developer. All Rights Reserved. # # Contributor(s): # # Alternatively, the contents of this file may be used under the terms of # either the GNU General Public License Version 2 or later (the "GPL"), or # the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), # in which case the provisions of the GPL or the LGPL are applicable instead # of those above. If you wish to allow use of your version of this file only # under the terms of either the GPL or the LGPL, and not to allow others to # use your version of this file under the terms of the MPL, indicate your # decision by deleting the provisions above and replace them with the notice # and other provisions required by the GPL or the LGPL. If you do not delete # the provisions above, a recipient may use your version of this file under # the terms of any one of the MPL, the GPL or the LGPL. # # ***** END LICENSE BLOCK ***** # # Config stuff for HPUX # # CC = gcc # CCC = g++ # CFLAGS += -Wall -Wno-format -fPIC CC = cc -Ae +Z CCC = CC -Ae +a1 +eh +Z RANLIB = echo MKSHLIB = $(LD) -b SO_SUFFIX = sl #.c.o: # $(CC) -c -MD $*.d $(CFLAGS) $< CPU_ARCH = hppa GFX_ARCH = x OS_CFLAGS = -DXP_UNIX -DHPUX -DSYSV -DHAVE_LOCALTIME_R OS_LIBS = -ldld ifeq ($(OS_RELEASE),B.10) PLATFORM_FLAGS += -DHPUX10 -Dhpux10 PORT_FLAGS += -DRW_NO_OVERLOAD_SCHAR -DHAVE_MODEL_H ifeq ($(OS_VERSION),.10) PLATFORM_FLAGS += -DHPUX10_10 endif ifeq ($(OS_VERSION),.20) PLATFORM_FLAGS += -DHPUX10_20 endif ifeq ($(OS_VERSION),.30) PLATFORM_FLAGS += -DHPUX10_30 endif endif pacparser-1.4.5/src/spidermonkey/js/src/config/HP-UXB.11.00.mk000066400000000000000000000047151464010763600234210ustar00rootroot00000000000000# -*- Mode: makefile -*- # # ***** BEGIN LICENSE BLOCK ***** # Version: MPL 1.1/GPL 2.0/LGPL 2.1 # # The contents of this file are subject to the Mozilla Public License Version # 1.1 (the "License"); you may not use this file except in compliance with # the License. You may obtain a copy of the License at # http://www.mozilla.org/MPL/ # # Software distributed under the License is distributed on an "AS IS" basis, # WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License # for the specific language governing rights and limitations under the # License. # # The Original Code is Mozilla Communicator client code, released # March 31, 1998. # # The Initial Developer of the Original Code is # Netscape Communications Corporation. # Portions created by the Initial Developer are Copyright (C) 1998 # the Initial Developer. All Rights Reserved. # # Contributor(s): # # Alternatively, the contents of this file may be used under the terms of # either the GNU General Public License Version 2 or later (the "GPL"), or # the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), # in which case the provisions of the GPL or the LGPL are applicable instead # of those above. If you wish to allow use of your version of this file only # under the terms of either the GPL or the LGPL, and not to allow others to # use your version of this file under the terms of the MPL, indicate your # decision by deleting the provisions above and replace them with the notice # and other provisions required by the GPL or the LGPL. If you do not delete # the provisions above, a recipient may use your version of this file under # the terms of any one of the MPL, the GPL or the LGPL. # # ***** END LICENSE BLOCK ***** # # Config stuff for HPUX # ifdef NS_USE_NATIVE CC = cc +Z +DAportable +DS2.0 +u4 # LD = aCC +Z -b -Wl,+s -Wl,-B,symbolic else CC = gcc -Wall -Wno-format -fPIC CCC = g++ -Wall -Wno-format -fPIC endif RANLIB = echo MKSHLIB = $(LD) -b SO_SUFFIX = sl #.c.o: # $(CC) -c -MD $*.d $(CFLAGS) $< CPU_ARCH = hppa GFX_ARCH = x OS_CFLAGS = -DXP_UNIX -DHPUX -DSYSV -D_HPUX -DNATIVE -D_POSIX_C_SOURCE=199506L -DHAVE_LOCALTIME_R OS_LIBS = -ldld XLDFLAGS = -lpthread ifeq ($(OS_RELEASE),B.10) PLATFORM_FLAGS += -DHPUX10 -Dhpux10 PORT_FLAGS += -DRW_NO_OVERLOAD_SCHAR -DHAVE_MODEL_H ifeq ($(OS_VERSION),.10) PLATFORM_FLAGS += -DHPUX10_10 endif ifeq ($(OS_VERSION),.20) PLATFORM_FLAGS += -DHPUX10_20 endif ifeq ($(OS_VERSION),.30) PLATFORM_FLAGS += -DHPUX10_30 endif endif pacparser-1.4.5/src/spidermonkey/js/src/config/IRIX.mk000066400000000000000000000047511464010763600225730ustar00rootroot00000000000000# -*- Mode: makefile -*- # # ***** BEGIN LICENSE BLOCK ***** # Version: MPL 1.1/GPL 2.0/LGPL 2.1 # # The contents of this file are subject to the Mozilla Public License Version # 1.1 (the "License"); you may not use this file except in compliance with # the License. You may obtain a copy of the License at # http://www.mozilla.org/MPL/ # # Software distributed under the License is distributed on an "AS IS" basis, # WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License # for the specific language governing rights and limitations under the # License. # # The Original Code is Mozilla Communicator client code, released # March 31, 1998. # # The Initial Developer of the Original Code is # Netscape Communications Corporation. # Portions created by the Initial Developer are Copyright (C) 1998 # the Initial Developer. All Rights Reserved. # # Contributor(s): # # Alternatively, the contents of this file may be used under the terms of # either the GNU General Public License Version 2 or later (the "GPL"), or # the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), # in which case the provisions of the GPL or the LGPL are applicable instead # of those above. If you wish to allow use of your version of this file only # under the terms of either the GPL or the LGPL, and not to allow others to # use your version of this file under the terms of the MPL, indicate your # decision by deleting the provisions above and replace them with the notice # and other provisions required by the GPL or the LGPL. If you do not delete # the provisions above, a recipient may use your version of this file under # the terms of any one of the MPL, the GPL or the LGPL. # # ***** END LICENSE BLOCK ***** # # Config stuff for IRIX # CPU_ARCH = mips GFX_ARCH = x RANLIB = /bin/true #NS_USE_GCC = 1 ifndef NS_USE_NATIVE CC = gcc CCC = g++ AS = $(CC) -x assembler-with-cpp ODD_CFLAGS = -Wall -Wno-format ifdef BUILD_OPT OPTIMIZER = -O6 endif else ifeq ($(OS_RELEASE),6.2) CC = cc -n32 -DIRIX6_2 endif ifeq ($(OS_RELEASE),6.3) CC = cc -n32 -DIRIX6_3 endif ifeq ($(OS_RELEASE),6.5) CC = cc -n32 -DIRIX6_5 endif CCC = CC # LD = CC ODD_CFLAGS = -fullwarn -xansi ifdef BUILD_OPT OPTIMIZER += -Olimit 4000 endif endif # For purify HAVE_PURIFY = 1 PURE_OS_CFLAGS = $(ODD_CFLAGS) -DXP_UNIX -DSVR4 -DSW_THREADS -DIRIX -DHAVE_LOCALTIME_R OS_CFLAGS = $(PURE_OS_CFLAGS) -MDupdate $(DEPENDENCIES) BSDECHO = echo MKSHLIB = $(LD) -n32 -shared # Use the editline library to provide line-editing support. JS_EDITLINE = 1 pacparser-1.4.5/src/spidermonkey/js/src/config/IRIX5.3.mk000066400000000000000000000034041464010763600230130ustar00rootroot00000000000000# -*- Mode: makefile -*- # # ***** BEGIN LICENSE BLOCK ***** # Version: MPL 1.1/GPL 2.0/LGPL 2.1 # # The contents of this file are subject to the Mozilla Public License Version # 1.1 (the "License"); you may not use this file except in compliance with # the License. You may obtain a copy of the License at # http://www.mozilla.org/MPL/ # # Software distributed under the License is distributed on an "AS IS" basis, # WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License # for the specific language governing rights and limitations under the # License. # # The Original Code is Mozilla Communicator client code, released # March 31, 1998. # # The Initial Developer of the Original Code is # Netscape Communications Corporation. # Portions created by the Initial Developer are Copyright (C) 1998 # the Initial Developer. All Rights Reserved. # # Contributor(s): # # Alternatively, the contents of this file may be used under the terms of # either the GNU General Public License Version 2 or later (the "GPL"), or # the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), # in which case the provisions of the GPL or the LGPL are applicable instead # of those above. If you wish to allow use of your version of this file only # under the terms of either the GPL or the LGPL, and not to allow others to # use your version of this file under the terms of the MPL, indicate your # decision by deleting the provisions above and replace them with the notice # and other provisions required by the GPL or the LGPL. If you do not delete # the provisions above, a recipient may use your version of this file under # the terms of any one of the MPL, the GPL or the LGPL. # # ***** END LICENSE BLOCK ***** # # Config stuff for IRIX5.3 # include $(DEPTH)/config/IRIX.mk pacparser-1.4.5/src/spidermonkey/js/src/config/IRIX6.1.mk000066400000000000000000000034041464010763600230120ustar00rootroot00000000000000# -*- Mode: makefile -*- # # ***** BEGIN LICENSE BLOCK ***** # Version: MPL 1.1/GPL 2.0/LGPL 2.1 # # The contents of this file are subject to the Mozilla Public License Version # 1.1 (the "License"); you may not use this file except in compliance with # the License. You may obtain a copy of the License at # http://www.mozilla.org/MPL/ # # Software distributed under the License is distributed on an "AS IS" basis, # WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License # for the specific language governing rights and limitations under the # License. # # The Original Code is Mozilla Communicator client code, released # March 31, 1998. # # The Initial Developer of the Original Code is # Netscape Communications Corporation. # Portions created by the Initial Developer are Copyright (C) 1998 # the Initial Developer. All Rights Reserved. # # Contributor(s): # # Alternatively, the contents of this file may be used under the terms of # either the GNU General Public License Version 2 or later (the "GPL"), or # the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), # in which case the provisions of the GPL or the LGPL are applicable instead # of those above. If you wish to allow use of your version of this file only # under the terms of either the GPL or the LGPL, and not to allow others to # use your version of this file under the terms of the MPL, indicate your # decision by deleting the provisions above and replace them with the notice # and other provisions required by the GPL or the LGPL. If you do not delete # the provisions above, a recipient may use your version of this file under # the terms of any one of the MPL, the GPL or the LGPL. # # ***** END LICENSE BLOCK ***** # # Config stuff for IRIX6.3 # include $(DEPTH)/config/IRIX.mk pacparser-1.4.5/src/spidermonkey/js/src/config/IRIX6.2.mk000066400000000000000000000034041464010763600230130ustar00rootroot00000000000000# -*- Mode: makefile -*- # # ***** BEGIN LICENSE BLOCK ***** # Version: MPL 1.1/GPL 2.0/LGPL 2.1 # # The contents of this file are subject to the Mozilla Public License Version # 1.1 (the "License"); you may not use this file except in compliance with # the License. You may obtain a copy of the License at # http://www.mozilla.org/MPL/ # # Software distributed under the License is distributed on an "AS IS" basis, # WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License # for the specific language governing rights and limitations under the # License. # # The Original Code is Mozilla Communicator client code, released # March 31, 1998. # # The Initial Developer of the Original Code is # Netscape Communications Corporation. # Portions created by the Initial Developer are Copyright (C) 1998 # the Initial Developer. All Rights Reserved. # # Contributor(s): # # Alternatively, the contents of this file may be used under the terms of # either the GNU General Public License Version 2 or later (the "GPL"), or # the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), # in which case the provisions of the GPL or the LGPL are applicable instead # of those above. If you wish to allow use of your version of this file only # under the terms of either the GPL or the LGPL, and not to allow others to # use your version of this file under the terms of the MPL, indicate your # decision by deleting the provisions above and replace them with the notice # and other provisions required by the GPL or the LGPL. If you do not delete # the provisions above, a recipient may use your version of this file under # the terms of any one of the MPL, the GPL or the LGPL. # # ***** END LICENSE BLOCK ***** # # Config stuff for IRIX6.3 # include $(DEPTH)/config/IRIX.mk pacparser-1.4.5/src/spidermonkey/js/src/config/IRIX6.3.mk000066400000000000000000000034041464010763600230140ustar00rootroot00000000000000# -*- Mode: makefile -*- # # ***** BEGIN LICENSE BLOCK ***** # Version: MPL 1.1/GPL 2.0/LGPL 2.1 # # The contents of this file are subject to the Mozilla Public License Version # 1.1 (the "License"); you may not use this file except in compliance with # the License. You may obtain a copy of the License at # http://www.mozilla.org/MPL/ # # Software distributed under the License is distributed on an "AS IS" basis, # WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License # for the specific language governing rights and limitations under the # License. # # The Original Code is Mozilla Communicator client code, released # March 31, 1998. # # The Initial Developer of the Original Code is # Netscape Communications Corporation. # Portions created by the Initial Developer are Copyright (C) 1998 # the Initial Developer. All Rights Reserved. # # Contributor(s): # # Alternatively, the contents of this file may be used under the terms of # either the GNU General Public License Version 2 or later (the "GPL"), or # the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), # in which case the provisions of the GPL or the LGPL are applicable instead # of those above. If you wish to allow use of your version of this file only # under the terms of either the GPL or the LGPL, and not to allow others to # use your version of this file under the terms of the MPL, indicate your # decision by deleting the provisions above and replace them with the notice # and other provisions required by the GPL or the LGPL. If you do not delete # the provisions above, a recipient may use your version of this file under # the terms of any one of the MPL, the GPL or the LGPL. # # ***** END LICENSE BLOCK ***** # # Config stuff for IRIX6.3 # include $(DEPTH)/config/IRIX.mk pacparser-1.4.5/src/spidermonkey/js/src/config/IRIX6.5.mk000066400000000000000000000034041464010763600230160ustar00rootroot00000000000000# -*- Mode: makefile -*- # # ***** BEGIN LICENSE BLOCK ***** # Version: MPL 1.1/GPL 2.0/LGPL 2.1 # # The contents of this file are subject to the Mozilla Public License Version # 1.1 (the "License"); you may not use this file except in compliance with # the License. You may obtain a copy of the License at # http://www.mozilla.org/MPL/ # # Software distributed under the License is distributed on an "AS IS" basis, # WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License # for the specific language governing rights and limitations under the # License. # # The Original Code is Mozilla Communicator client code, released # March 31, 1998. # # The Initial Developer of the Original Code is # Netscape Communications Corporation. # Portions created by the Initial Developer are Copyright (C) 1998 # the Initial Developer. All Rights Reserved. # # Contributor(s): # # Alternatively, the contents of this file may be used under the terms of # either the GNU General Public License Version 2 or later (the "GPL"), or # the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), # in which case the provisions of the GPL or the LGPL are applicable instead # of those above. If you wish to allow use of your version of this file only # under the terms of either the GPL or the LGPL, and not to allow others to # use your version of this file under the terms of the MPL, indicate your # decision by deleting the provisions above and replace them with the notice # and other provisions required by the GPL or the LGPL. If you do not delete # the provisions above, a recipient may use your version of this file under # the terms of any one of the MPL, the GPL or the LGPL. # # ***** END LICENSE BLOCK ***** # # Config stuff for IRIX6.3 # include $(DEPTH)/config/IRIX.mk pacparser-1.4.5/src/spidermonkey/js/src/config/Linux_All.mk000066400000000000000000000060711464010763600237040ustar00rootroot00000000000000# -*- Mode: makefile -*- # # ***** BEGIN LICENSE BLOCK ***** # Version: MPL 1.1/GPL 2.0/LGPL 2.1 # # The contents of this file are subject to the Mozilla Public License Version # 1.1 (the "License"); you may not use this file except in compliance with # the License. You may obtain a copy of the License at # http://www.mozilla.org/MPL/ # # Software distributed under the License is distributed on an "AS IS" basis, # WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License # for the specific language governing rights and limitations under the # License. # # The Original Code is Mozilla Communicator client code, released # March 31, 1998. # # The Initial Developer of the Original Code is # Netscape Communications Corporation. # Portions created by the Initial Developer are Copyright (C) 1998 # the Initial Developer. All Rights Reserved. # # Contributor(s): # # Alternatively, the contents of this file may be used under the terms of # either the GNU General Public License Version 2 or later (the "GPL"), or # the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), # in which case the provisions of the GPL or the LGPL are applicable instead # of those above. If you wish to allow use of your version of this file only # under the terms of either the GPL or the LGPL, and not to allow others to # use your version of this file under the terms of the MPL, indicate your # decision by deleting the provisions above and replace them with the notice # and other provisions required by the GPL or the LGPL. If you do not delete # the provisions above, a recipient may use your version of this file under # the terms of any one of the MPL, the GPL or the LGPL. # # ***** END LICENSE BLOCK ***** # # Config for all versions of Linux # CC = gcc CCC = g++ CFLAGS += -Wall -Wno-format OS_CFLAGS = -DXP_UNIX -DSVR4 -DSYSV -D_BSD_SOURCE -DPOSIX_SOURCE -DHAVE_LOCALTIME_R RANLIB = echo MKSHLIB = $(LD) -shared $(XMKSHLIBOPTS) #.c.o: # $(CC) -c -MD $*.d $(CFLAGS) $< CPU_ARCH = $(shell uname -m) # don't filter in x86-64 architecture ifneq (x86_64,$(CPU_ARCH)) ifeq (86,$(findstring 86,$(CPU_ARCH))) CPU_ARCH = x86 OS_CFLAGS+= -DX86_LINUX ifeq (gcc, $(CC)) # if using gcc on x86, check version for opt bug # (http://bugzilla.mozilla.org/show_bug.cgi?id=24892) GCC_VERSION := $(shell gcc -v 2>&1 | grep version | awk '{ print $$3 }') GCC_LIST:=$(sort 2.91.66 $(GCC_VERSION) ) ifeq (2.91.66, $(firstword $(GCC_LIST))) CFLAGS+= -DGCC_OPT_BUG endif endif endif endif GFX_ARCH = x OS_LIBS = -lm -lc ASFLAGS += -x assembler-with-cpp ifeq ($(CPU_ARCH),alpha) # Ask the C compiler on alpha linux to let us work with denormalized # double values, which are required by the ECMA spec. OS_CFLAGS += -mieee endif # Use the editline library to provide line-editing support. JS_EDITLINE = 1 ifeq ($(CPU_ARCH),x86_64) # Use VA_COPY() standard macro on x86-64 # FIXME: better use it everywhere OS_CFLAGS += -DHAVE_VA_COPY -DVA_COPY=va_copy endif ifeq ($(CPU_ARCH),x86_64) # We need PIC code for shared libraries # FIXME: better patch rules.mk & fdlibm/Makefile* OS_CFLAGS += -DPIC -fPIC endif pacparser-1.4.5/src/spidermonkey/js/src/config/Mac_OS10.0.mk000077500000000000000000000050371464010763600234210ustar00rootroot00000000000000# -*- Mode: makefile -*- # # ***** BEGIN LICENSE BLOCK ***** # Version: MPL 1.1/GPL 2.0/LGPL 2.1 # # The contents of this file are subject to the Mozilla Public License Version # 1.1 (the "License"); you may not use this file except in compliance with # the License. You may obtain a copy of the License at # http://www.mozilla.org/MPL/ # # Software distributed under the License is distributed on an "AS IS" basis, # WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License # for the specific language governing rights and limitations under the # License. # # The Original Code is Mozilla Communicator client code, released # March 31, 1998. # # The Initial Developer of the Original Code is # Netscape Communications Corporation. # Portions created by the Initial Developer are Copyright (C) 1998 # the Initial Developer. All Rights Reserved. # # Contributor(s): # Steve Zellers (zellers@apple.com) # # Alternatively, the contents of this file may be used under the terms of # either the GNU General Public License Version 2 or later (the "GPL"), or # the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), # in which case the provisions of the GPL or the LGPL are applicable instead # of those above. If you wish to allow use of your version of this file only # under the terms of either the GPL or the LGPL, and not to allow others to # use your version of this file under the terms of the MPL, indicate your # decision by deleting the provisions above and replace them with the notice # and other provisions required by the GPL or the LGPL. If you do not delete # the provisions above, a recipient may use your version of this file under # the terms of any one of the MPL, the GPL or the LGPL. # # ***** END LICENSE BLOCK ***** # # Config for Mac OS X as of PR3 # Just ripped from Linux config # CC = cc CCC = g++ CFLAGS += -Wall -Wno-format OS_CFLAGS = -DXP_UNIX -DSVR4 -DSYSV -D_BSD_SOURCE -DPOSIX_SOURCE -DRHAPSODY RANLIB = ranlib MKSHLIB = libtool -dynamic $(XMKSHLIBOPTS) -framework System #.c.o: # $(CC) -c -MD $*.d $(CFLAGS) $< CPU_ARCH = $(shell uname -m) ifeq (86,$(findstring 86,$(CPU_ARCH))) CPU_ARCH = x86 OS_CFLAGS+= -DX86_LINUX endif GFX_ARCH = x OS_LIBS = -lc -framework System ASFLAGS += -x assembler-with-cpp ifeq ($(CPU_ARCH),alpha) # Ask the C compiler on alpha linux to let us work with denormalized # double values, which are required by the ECMA spec. OS_CFLAGS += -mieee endif # Use the editline library to provide line-editing support. JS_EDITLINE = 1 # Don't allow Makefile.ref to use libmath NO_LIBM = 1 pacparser-1.4.5/src/spidermonkey/js/src/config/OSF1V4.0.mk000066400000000000000000000046231464010763600230760ustar00rootroot00000000000000# -*- Mode: makefile -*- # # ***** BEGIN LICENSE BLOCK ***** # Version: MPL 1.1/GPL 2.0/LGPL 2.1 # # The contents of this file are subject to the Mozilla Public License Version # 1.1 (the "License"); you may not use this file except in compliance with # the License. You may obtain a copy of the License at # http://www.mozilla.org/MPL/ # # Software distributed under the License is distributed on an "AS IS" basis, # WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License # for the specific language governing rights and limitations under the # License. # # The Original Code is Mozilla Communicator client code, released # March 31, 1998. # # The Initial Developer of the Original Code is # Netscape Communications Corporation. # Portions created by the Initial Developer are Copyright (C) 1998 # the Initial Developer. All Rights Reserved. # # Contributor(s): # # Alternatively, the contents of this file may be used under the terms of # either the GNU General Public License Version 2 or later (the "GPL"), or # the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), # in which case the provisions of the GPL or the LGPL are applicable instead # of those above. If you wish to allow use of your version of this file only # under the terms of either the GPL or the LGPL, and not to allow others to # use your version of this file under the terms of the MPL, indicate your # decision by deleting the provisions above and replace them with the notice # and other provisions required by the GPL or the LGPL. If you do not delete # the provisions above, a recipient may use your version of this file under # the terms of any one of the MPL, the GPL or the LGPL. # # ***** END LICENSE BLOCK ***** # # Config stuff for Data General DG/UX # # # Initial DG/UX port by Marc Fraioli (fraioli@dg-rtp.dg.com) # ifndef NS_USE_NATIVE CC = gcc CCC = g++ CFLAGS += -mieee -Wall -Wno-format else CC = cc CCC = cxx CFLAGS += -ieee -std # LD = cxx endif RANLIB = echo MKSHLIB = $(LD) -shared -taso -all -expect_unresolved "*" # # _DGUX_SOURCE is needed to turn on a lot of stuff in the headers if # you're not using DG's compiler. It shouldn't hurt if you are. # # _POSIX4A_DRAFT10_SOURCE is needed to pick up localtime_r, used in # prtime.c # OS_CFLAGS = -DXP_UNIX -DSVR4 -DSYSV -DDGUX -D_DGUX_SOURCE -D_POSIX4A_DRAFT10_SOURCE -DOSF1 -DHAVE_LOCALTIME_R OS_LIBS = -lsocket -lnsl NOSUCHFILE = /no-such-file pacparser-1.4.5/src/spidermonkey/js/src/config/OSF1V5.0.mk000066400000000000000000000043551464010763600231010ustar00rootroot00000000000000# -*- Mode: makefile -*- # # ***** BEGIN LICENSE BLOCK ***** # Version: MPL 1.1/GPL 2.0/LGPL 2.1 # # The contents of this file are subject to the Mozilla Public License Version # 1.1 (the "License"); you may not use this file except in compliance with # the License. You may obtain a copy of the License at # http://www.mozilla.org/MPL/ # # Software distributed under the License is distributed on an "AS IS" basis, # WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License # for the specific language governing rights and limitations under the # License. # # The Original Code is Mozilla Communicator client code, released # March 31, 1998. # # The Initial Developer of the Original Code is # Netscape Communications Corporation. # Portions created by the Initial Developer are Copyright (C) 1998 # the Initial Developer. All Rights Reserved. # # Contributor(s): # # Alternatively, the contents of this file may be used under the terms of # either the GNU General Public License Version 2 or later (the "GPL"), or # the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), # in which case the provisions of the GPL or the LGPL are applicable instead # of those above. If you wish to allow use of your version of this file only # under the terms of either the GPL or the LGPL, and not to allow others to # use your version of this file under the terms of the MPL, indicate your # decision by deleting the provisions above and replace them with the notice # and other provisions required by the GPL or the LGPL. If you do not delete # the provisions above, a recipient may use your version of this file under # the terms of any one of the MPL, the GPL or the LGPL. # # ***** END LICENSE BLOCK ***** # # Config stuff for Tru64 Unix 5.0 # # # Initial DG/UX port by Marc Fraioli (fraioli@dg-rtp.dg.com) # ifndef NS_USE_NATIVE CC = gcc CCC = g++ CFLAGS += -mieee -Wall -Wno-format else CC = cc CCC = cxx CFLAGS += -ieee -std -pthread # LD = cxx endif RANLIB = echo MKSHLIB = $(LD) -shared -all -expect_unresolved "*" # # _POSIX4A_DRAFT10_SOURCE is needed to pick up localtime_r, used in # prtime.c # OS_CFLAGS = -DXP_UNIX -DSVR4 -DSYSV -D_POSIX4A_DRAFT10_SOURCE -DOSF1 -DHAVE_LOCALTIME_R OS_LIBS = -lsocket -lnsl NOSUCHFILE = /no-such-file pacparser-1.4.5/src/spidermonkey/js/src/config/SunOS4.1.4.mk000066400000000000000000000055231464010763600234120ustar00rootroot00000000000000# -*- Mode: makefile -*- # # ***** BEGIN LICENSE BLOCK ***** # Version: MPL 1.1/GPL 2.0/LGPL 2.1 # # The contents of this file are subject to the Mozilla Public License Version # 1.1 (the "License"); you may not use this file except in compliance with # the License. You may obtain a copy of the License at # http://www.mozilla.org/MPL/ # # Software distributed under the License is distributed on an "AS IS" basis, # WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License # for the specific language governing rights and limitations under the # License. # # The Original Code is Mozilla Communicator client code, released # March 31, 1998. # # The Initial Developer of the Original Code is # Netscape Communications Corporation. # Portions created by the Initial Developer are Copyright (C) 1998 # the Initial Developer. All Rights Reserved. # # Contributor(s): # # Alternatively, the contents of this file may be used under the terms of # either the GNU General Public License Version 2 or later (the "GPL"), or # the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), # in which case the provisions of the GPL or the LGPL are applicable instead # of those above. If you wish to allow use of your version of this file only # under the terms of either the GPL or the LGPL, and not to allow others to # use your version of this file under the terms of the MPL, indicate your # decision by deleting the provisions above and replace them with the notice # and other provisions required by the GPL or the LGPL. If you do not delete # the provisions above, a recipient may use your version of this file under # the terms of any one of the MPL, the GPL or the LGPL. # # ***** END LICENSE BLOCK ***** # # Config stuff for SunOS4.1 # CC = gcc CCC = g++ RANLIB = ranlib #.c.o: # $(CC) -c -MD $*.d $(CFLAGS) $< CPU_ARCH = sparc GFX_ARCH = x # A pile of -D's to build xfe on sunos MOZ_CFLAGS = -DSTRINGS_ALIGNED -DNO_REGEX -DNO_ISDIR -DUSE_RE_COMP \ -DNO_REGCOMP -DUSE_GETWD -DNO_MEMMOVE -DNO_ALLOCA \ -DBOGUS_MB_MAX -DNO_CONST # Purify doesn't like -MDupdate NOMD_OS_CFLAGS = -DXP_UNIX -Wall -Wno-format -DSW_THREADS -DSUNOS4 -DNEED_SYSCALL \ $(MOZ_CFLAGS) OS_CFLAGS = $(NOMD_OS_CFLAGS) -MDupdate $(DEPENDENCIES) OS_LIBS = -ldl -lm MKSHLIB = $(LD) -L$(MOTIF)/lib HAVE_PURIFY = 1 MOTIF = /home/motif/usr MOTIFLIB = -L$(MOTIF)/lib -lXm INCLUDES += -I/usr/X11R5/include -I$(MOTIF)/include NOSUCHFILE = /solaris-rm-f-sucks LOCALE_MAP = $(DEPTH)/cmd/xfe/intl/sunos.lm EN_LOCALE = en_US DE_LOCALE = de FR_LOCALE = fr JP_LOCALE = ja SJIS_LOCALE = ja_JP.SJIS KR_LOCALE = ko CN_LOCALE = zh TW_LOCALE = zh_TW I2_LOCALE = i2 IT_LOCALE = it SV_LOCALE = sv ES_LOCALE = es NL_LOCALE = nl PT_LOCALE = pt LOC_LIB_DIR = /usr/openwin/lib/locale BSDECHO = echo # # These defines are for building unix plugins # BUILD_UNIX_PLUGINS = 1 DSO_LDOPTS = DSO_LDFLAGS = pacparser-1.4.5/src/spidermonkey/js/src/config/SunOS5.3.mk000066400000000000000000000051421464010763600232500ustar00rootroot00000000000000# -*- Mode: makefile -*- # # ***** BEGIN LICENSE BLOCK ***** # Version: MPL 1.1/GPL 2.0/LGPL 2.1 # # The contents of this file are subject to the Mozilla Public License Version # 1.1 (the "License"); you may not use this file except in compliance with # the License. You may obtain a copy of the License at # http://www.mozilla.org/MPL/ # # Software distributed under the License is distributed on an "AS IS" basis, # WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License # for the specific language governing rights and limitations under the # License. # # The Original Code is Mozilla Communicator client code, released # March 31, 1998. # # The Initial Developer of the Original Code is # Netscape Communications Corporation. # Portions created by the Initial Developer are Copyright (C) 1998 # the Initial Developer. All Rights Reserved. # # Contributor(s): # # Alternatively, the contents of this file may be used under the terms of # either the GNU General Public License Version 2 or later (the "GPL"), or # the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), # in which case the provisions of the GPL or the LGPL are applicable instead # of those above. If you wish to allow use of your version of this file only # under the terms of either the GPL or the LGPL, and not to allow others to # use your version of this file under the terms of the MPL, indicate your # decision by deleting the provisions above and replace them with the notice # and other provisions required by the GPL or the LGPL. If you do not delete # the provisions above, a recipient may use your version of this file under # the terms of any one of the MPL, the GPL or the LGPL. # # ***** END LICENSE BLOCK ***** # # Config stuff for SunOS5.3 # CC = gcc CCC = g++ CFLAGS += -Wall -Wno-format #CC = /opt/SUNWspro/SC3.0.1/bin/cc RANLIB = echo #.c.o: # $(CC) -c -MD $*.d $(CFLAGS) $< CPU_ARCH = sparc GFX_ARCH = x OS_CFLAGS = -DXP_UNIX -DSVR4 -DSYSV -DSOLARIS -DHAVE_LOCALTIME_R OS_LIBS = -lsocket -lnsl -ldl ASFLAGS += -P -L -K PIC -D_ASM -D__STDC__=0 HAVE_PURIFY = 1 NOSUCHFILE = /solaris-rm-f-sucks ifndef JS_NO_ULTRA ULTRA_OPTIONS := -xarch=v8plus ULTRA_OPTIONSD := -DULTRA_SPARC else ULTRA_OPTIONS := -xarch=v8 ULTRA_OPTIONSD := endif ifeq ($(OS_CPUARCH),sun4u) DEFINES += $(ULTRA_OPTIONSD) ifeq ($(findstring gcc,$(CC)),gcc) DEFINES += -Wa,$(ULTRA_OPTIONS),$(ULTRA_OPTIONSD) else ASFLAGS += $(ULTRA_OPTIONS) $(ULTRA_OPTIONSD) endif endif ifeq ($(OS_CPUARCH),sun4m) ifeq ($(findstring gcc,$(CC)),gcc) DEFINES += -Wa,-xarch=v8 else ASFLAGS += -xarch=v8 endif endif MKSHLIB = $(LD) -G pacparser-1.4.5/src/spidermonkey/js/src/config/SunOS5.4.mk000066400000000000000000000051161464010763600232520ustar00rootroot00000000000000# -*- Mode: makefile -*- # # ***** BEGIN LICENSE BLOCK ***** # Version: MPL 1.1/GPL 2.0/LGPL 2.1 # # The contents of this file are subject to the Mozilla Public License Version # 1.1 (the "License"); you may not use this file except in compliance with # the License. You may obtain a copy of the License at # http://www.mozilla.org/MPL/ # # Software distributed under the License is distributed on an "AS IS" basis, # WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License # for the specific language governing rights and limitations under the # License. # # The Original Code is Mozilla Communicator client code, released # March 31, 1998. # # The Initial Developer of the Original Code is # Netscape Communications Corporation. # Portions created by the Initial Developer are Copyright (C) 1998 # the Initial Developer. All Rights Reserved. # # Contributor(s): # # Alternatively, the contents of this file may be used under the terms of # either the GNU General Public License Version 2 or later (the "GPL"), or # the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), # in which case the provisions of the GPL or the LGPL are applicable instead # of those above. If you wish to allow use of your version of this file only # under the terms of either the GPL or the LGPL, and not to allow others to # use your version of this file under the terms of the MPL, indicate your # decision by deleting the provisions above and replace them with the notice # and other provisions required by the GPL or the LGPL. If you do not delete # the provisions above, a recipient may use your version of this file under # the terms of any one of the MPL, the GPL or the LGPL. # # ***** END LICENSE BLOCK ***** # # Config stuff for SunOS5.4 # ifdef NS_USE_NATIVE CC = cc CCC = CC else CC = gcc CCC = g++ CFLAGS += -Wall -Wno-format endif RANLIB = echo CPU_ARCH = sparc GFX_ARCH = x OS_CFLAGS = -DXP_UNIX -DSVR4 -DSYSV -D__svr4 -DSOLARIS -DHAVE_LOCALTIME_R OS_LIBS = -lsocket -lnsl -ldl ASFLAGS += -P -L -K PIC -D_ASM -D__STDC__=0 HAVE_PURIFY = 1 NOSUCHFILE = /solaris-rm-f-sucks ifndef JS_NO_ULTRA ULTRA_OPTIONS := -xarch=v8plus ULTRA_OPTIONSD := -DULTRA_SPARC else ULTRA_OPTIONS := -xarch=v8 ULTRA_OPTIONSD := endif ifeq ($(OS_CPUARCH),sun4u) DEFINES += $(ULTRA_OPTIONSD) ifeq ($(findstring gcc,$(CC)),gcc) DEFINES += -Wa,$(ULTRA_OPTIONS),$(ULTRA_OPTIONSD) else ASFLAGS += $(ULTRA_OPTIONS) $(ULTRA_OPTIONSD) endif endif ifeq ($(OS_CPUARCH),sun4m) ifeq ($(findstring gcc,$(CC)),gcc) DEFINES += -Wa,-xarch=v8 else ASFLAGS += -xarch=v8 endif endif MKSHLIB = $(LD) -G pacparser-1.4.5/src/spidermonkey/js/src/config/SunOS5.5.1.mk000066400000000000000000000034131464010763600234100ustar00rootroot00000000000000# -*- Mode: makefile -*- # # ***** BEGIN LICENSE BLOCK ***** # Version: MPL 1.1/GPL 2.0/LGPL 2.1 # # The contents of this file are subject to the Mozilla Public License Version # 1.1 (the "License"); you may not use this file except in compliance with # the License. You may obtain a copy of the License at # http://www.mozilla.org/MPL/ # # Software distributed under the License is distributed on an "AS IS" basis, # WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License # for the specific language governing rights and limitations under the # License. # # The Original Code is Mozilla Communicator client code, released # March 31, 1998. # # The Initial Developer of the Original Code is # Netscape Communications Corporation. # Portions created by the Initial Developer are Copyright (C) 1998 # the Initial Developer. All Rights Reserved. # # Contributor(s): # # Alternatively, the contents of this file may be used under the terms of # either the GNU General Public License Version 2 or later (the "GPL"), or # the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), # in which case the provisions of the GPL or the LGPL are applicable instead # of those above. If you wish to allow use of your version of this file only # under the terms of either the GPL or the LGPL, and not to allow others to # use your version of this file under the terms of the MPL, indicate your # decision by deleting the provisions above and replace them with the notice # and other provisions required by the GPL or the LGPL. If you do not delete # the provisions above, a recipient may use your version of this file under # the terms of any one of the MPL, the GPL or the LGPL. # # ***** END LICENSE BLOCK ***** # # Config stuff for SunOS5.5.1 # include $(DEPTH)/config/SunOS5.5.mk pacparser-1.4.5/src/spidermonkey/js/src/config/SunOS5.5.mk000066400000000000000000000050261464010763600232530ustar00rootroot00000000000000# -*- Mode: makefile -*- # # ***** BEGIN LICENSE BLOCK ***** # Version: MPL 1.1/GPL 2.0/LGPL 2.1 # # The contents of this file are subject to the Mozilla Public License Version # 1.1 (the "License"); you may not use this file except in compliance with # the License. You may obtain a copy of the License at # http://www.mozilla.org/MPL/ # # Software distributed under the License is distributed on an "AS IS" basis, # WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License # for the specific language governing rights and limitations under the # License. # # The Original Code is Mozilla Communicator client code, released # March 31, 1998. # # The Initial Developer of the Original Code is # Netscape Communications Corporation. # Portions created by the Initial Developer are Copyright (C) 1998 # the Initial Developer. All Rights Reserved. # # Contributor(s): # # Alternatively, the contents of this file may be used under the terms of # either the GNU General Public License Version 2 or later (the "GPL"), or # the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), # in which case the provisions of the GPL or the LGPL are applicable instead # of those above. If you wish to allow use of your version of this file only # under the terms of either the GPL or the LGPL, and not to allow others to # use your version of this file under the terms of the MPL, indicate your # decision by deleting the provisions above and replace them with the notice # and other provisions required by the GPL or the LGPL. If you do not delete # the provisions above, a recipient may use your version of this file under # the terms of any one of the MPL, the GPL or the LGPL. # # ***** END LICENSE BLOCK ***** # # Config stuff for SunOS5.5 # AS = /usr/ccs/bin/as ifndef NS_USE_NATIVE CC = gcc CCC = g++ CFLAGS += -Wall -Wno-format else CC = cc CCC = CC endif RANLIB = echo #.c.o: # $(CC) -c -MD $*.d $(CFLAGS) $< CPU_ARCH = sparc GFX_ARCH = x OS_CFLAGS = -DXP_UNIX -DSVR4 -DSYSV -DSOLARIS -DHAVE_LOCALTIME_R OS_LIBS = -lsocket -lnsl -ldl ASFLAGS += -P -L -K PIC -D_ASM -D__STDC__=0 HAVE_PURIFY = 1 NOSUCHFILE = /solaris-rm-f-sucks ifeq ($(OS_CPUARCH),sun4u) # ultra sparc? ifeq ($(CC),gcc) # using gcc? ifndef JS_NO_ULTRA # do we want ultra? ifdef JS_THREADSAFE # only in thread-safe mode DEFINES += -DULTRA_SPARC DEFINES += -Wa,-xarch=v8plus,-DULTRA_SPARC else ASFLAGS += -xarch=v8plus -DULTRA_SPARC endif endif endif endif MKSHLIB = $(LD) -G # Use the editline library to provide line-editing support. JS_EDITLINE = 1 pacparser-1.4.5/src/spidermonkey/js/src/config/SunOS5.6.mk000066400000000000000000000051011464010763600232460ustar00rootroot00000000000000# -*- Mode: makefile -*- # # ***** BEGIN LICENSE BLOCK ***** # Version: MPL 1.1/GPL 2.0/LGPL 2.1 # # The contents of this file are subject to the Mozilla Public License Version # 1.1 (the "License"); you may not use this file except in compliance with # the License. You may obtain a copy of the License at # http://www.mozilla.org/MPL/ # # Software distributed under the License is distributed on an "AS IS" basis, # WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License # for the specific language governing rights and limitations under the # License. # # The Original Code is Mozilla Communicator client code, released # March 31, 1998. # # The Initial Developer of the Original Code is # Netscape Communications Corporation. # Portions created by the Initial Developer are Copyright (C) 1998 # the Initial Developer. All Rights Reserved. # # Contributor(s): # # Alternatively, the contents of this file may be used under the terms of # either the GNU General Public License Version 2 or later (the "GPL"), or # the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), # in which case the provisions of the GPL or the LGPL are applicable instead # of those above. If you wish to allow use of your version of this file only # under the terms of either the GPL or the LGPL, and not to allow others to # use your version of this file under the terms of the MPL, indicate your # decision by deleting the provisions above and replace them with the notice # and other provisions required by the GPL or the LGPL. If you do not delete # the provisions above, a recipient may use your version of this file under # the terms of any one of the MPL, the GPL or the LGPL. # # ***** END LICENSE BLOCK ***** # # Config stuff for SunOS5.5 # AS = /usr/ccs/bin/as ifndef NS_USE_NATIVE CC = gcc CCC = g++ CFLAGS += -Wall -Wno-format else CC = cc CCC = CC CFLAGS += -mt -KPIC # LD = CC endif RANLIB = echo #.c.o: # $(CC) -c -MD $*.d $(CFLAGS) $< CPU_ARCH = sparc GFX_ARCH = x OS_CFLAGS = -DXP_UNIX -DSVR4 -DSYSV -DSOLARIS -DHAVE_LOCALTIME_R OS_LIBS = -lsocket -lnsl -ldl ASFLAGS += -P -L -K PIC -D_ASM -D__STDC__=0 HAVE_PURIFY = 1 NOSUCHFILE = /solaris-rm-f-sucks ifeq ($(OS_CPUARCH),sun4u) # ultra sparc? ifeq ($(CC),gcc) # using gcc? ifndef JS_NO_ULTRA # do we want ultra? ifdef JS_THREADSAFE # only in thread-safe mode DEFINES += -DULTRA_SPARC DEFINES += -Wa,-xarch=v8plus,-DULTRA_SPARC else ASFLAGS += -xarch=v8plus -DULTRA_SPARC endif endif endif endif MKSHLIB = $(LD) -G # Use the editline library to provide line-editing support. JS_EDITLINE = 1 pacparser-1.4.5/src/spidermonkey/js/src/config/SunOS5.7.mk000066400000000000000000000034111464010763600232510ustar00rootroot00000000000000# -*- Mode: makefile -*- # # ***** BEGIN LICENSE BLOCK ***** # Version: MPL 1.1/GPL 2.0/LGPL 2.1 # # The contents of this file are subject to the Mozilla Public License Version # 1.1 (the "License"); you may not use this file except in compliance with # the License. You may obtain a copy of the License at # http://www.mozilla.org/MPL/ # # Software distributed under the License is distributed on an "AS IS" basis, # WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License # for the specific language governing rights and limitations under the # License. # # The Original Code is Mozilla Communicator client code, released # March 31, 1998. # # The Initial Developer of the Original Code is # Netscape Communications Corporation. # Portions created by the Initial Developer are Copyright (C) 1999 # the Initial Developer. All Rights Reserved. # # Contributor(s): # # Alternatively, the contents of this file may be used under the terms of # either the GNU General Public License Version 2 or later (the "GPL"), or # the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), # in which case the provisions of the GPL or the LGPL are applicable instead # of those above. If you wish to allow use of your version of this file only # under the terms of either the GPL or the LGPL, and not to allow others to # use your version of this file under the terms of the MPL, indicate your # decision by deleting the provisions above and replace them with the notice # and other provisions required by the GPL or the LGPL. If you do not delete # the provisions above, a recipient may use your version of this file under # the terms of any one of the MPL, the GPL or the LGPL. # # ***** END LICENSE BLOCK ***** # # Config stuff for SunOS5.7 # include $(DEPTH)/config/SunOS5.5.mk pacparser-1.4.5/src/spidermonkey/js/src/config/SunOS5.8.mk000066400000000000000000000034111464010763600232520ustar00rootroot00000000000000# -*- Mode: makefile -*- # # ***** BEGIN LICENSE BLOCK ***** # Version: MPL 1.1/GPL 2.0/LGPL 2.1 # # The contents of this file are subject to the Mozilla Public License Version # 1.1 (the "License"); you may not use this file except in compliance with # the License. You may obtain a copy of the License at # http://www.mozilla.org/MPL/ # # Software distributed under the License is distributed on an "AS IS" basis, # WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License # for the specific language governing rights and limitations under the # License. # # The Original Code is Mozilla Communicator client code, released # March 31, 1998. # # The Initial Developer of the Original Code is # Netscape Communications Corporation. # Portions created by the Initial Developer are Copyright (C) 1999 # the Initial Developer. All Rights Reserved. # # Contributor(s): # # Alternatively, the contents of this file may be used under the terms of # either the GNU General Public License Version 2 or later (the "GPL"), or # the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), # in which case the provisions of the GPL or the LGPL are applicable instead # of those above. If you wish to allow use of your version of this file only # under the terms of either the GPL or the LGPL, and not to allow others to # use your version of this file under the terms of the MPL, indicate your # decision by deleting the provisions above and replace them with the notice # and other provisions required by the GPL or the LGPL. If you do not delete # the provisions above, a recipient may use your version of this file under # the terms of any one of the MPL, the GPL or the LGPL. # # ***** END LICENSE BLOCK ***** # # Config stuff for SunOS5.8 # include $(DEPTH)/config/SunOS5.5.mk pacparser-1.4.5/src/spidermonkey/js/src/config/SunOS5.9.mk000066400000000000000000000034111464010763600232530ustar00rootroot00000000000000# -*- Mode: makefile -*- # # ***** BEGIN LICENSE BLOCK ***** # Version: MPL 1.1/GPL 2.0/LGPL 2.1 # # The contents of this file are subject to the Mozilla Public License Version # 1.1 (the "License"); you may not use this file except in compliance with # the License. You may obtain a copy of the License at # http://www.mozilla.org/MPL/ # # Software distributed under the License is distributed on an "AS IS" basis, # WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License # for the specific language governing rights and limitations under the # License. # # The Original Code is Mozilla Communicator client code, released # March 31, 1998. # # The Initial Developer of the Original Code is # Netscape Communications Corporation. # Portions created by the Initial Developer are Copyright (C) 1999 # the Initial Developer. All Rights Reserved. # # Contributor(s): # # Alternatively, the contents of this file may be used under the terms of # either the GNU General Public License Version 2 or later (the "GPL"), or # the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), # in which case the provisions of the GPL or the LGPL are applicable instead # of those above. If you wish to allow use of your version of this file only # under the terms of either the GPL or the LGPL, and not to allow others to # use your version of this file under the terms of the MPL, indicate your # decision by deleting the provisions above and replace them with the notice # and other provisions required by the GPL or the LGPL. If you do not delete # the provisions above, a recipient may use your version of this file under # the terms of any one of the MPL, the GPL or the LGPL. # # ***** END LICENSE BLOCK ***** # # Config stuff for SunOS5.9 # include $(DEPTH)/config/SunOS5.5.mk pacparser-1.4.5/src/spidermonkey/js/src/config/WINNT4.0.mk000066400000000000000000000077121464010763600231410ustar00rootroot00000000000000# -*- Mode: makefile -*- # # ***** BEGIN LICENSE BLOCK ***** # Version: MPL 1.1/GPL 2.0/LGPL 2.1 # # The contents of this file are subject to the Mozilla Public License Version # 1.1 (the "License"); you may not use this file except in compliance with # the License. You may obtain a copy of the License at # http://www.mozilla.org/MPL/ # # Software distributed under the License is distributed on an "AS IS" basis, # WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License # for the specific language governing rights and limitations under the # License. # # The Original Code is Mozilla Communicator client code, released # March 31, 1998. # # The Initial Developer of the Original Code is # Netscape Communications Corporation. # Portions created by the Initial Developer are Copyright (C) 1998 # the Initial Developer. All Rights Reserved. # # Contributor(s): # # Alternatively, the contents of this file may be used under the terms of # either the GNU General Public License Version 2 or later (the "GPL"), or # the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), # in which case the provisions of the GPL or the LGPL are applicable instead # of those above. If you wish to allow use of your version of this file only # under the terms of either the GPL or the LGPL, and not to allow others to # use your version of this file under the terms of the MPL, indicate your # decision by deleting the provisions above and replace them with the notice # and other provisions required by the GPL or the LGPL. If you do not delete # the provisions above, a recipient may use your version of this file under # the terms of any one of the MPL, the GPL or the LGPL. # # ***** END LICENSE BLOCK ***** # # Config for Windows NT using MS Visual C++ (version?) # CC = cl RANLIB = echo PDBFILE = $(basename $(@F)).pdb #.c.o: # $(CC) -c -MD $*.d $(CFLAGS) $< CPU_ARCH = x86 # XXX fixme GFX_ARCH = win32 # MSVC compiler options for both debug/optimize # -nologo - suppress copyright message # -W3 - Warning level 3 # -Gm - enable minimal rebuild # -Z7 - put debug info into the executable, not in .pdb file # -Zi - put debug info into .pdb file # -YX - automatic precompiled headers # -GX - enable C++ exception support WIN_CFLAGS = -nologo -W3 # MSVC compiler options for debug builds linked to MSVCRTD.DLL # -MDd - link with MSVCRTD.LIB (Dynamically-linked, multi-threaded, debug C-runtime) # -Od - minimal optimization WIN_IDG_CFLAGS = -MDd -Od -Z7 # MSVC compiler options for debug builds linked to MSVCRT.DLL # -MD - link with MSVCRT.LIB (Dynamically-linked, multi-threaded, debug C-runtime) # -Od - minimal optimization WIN_DEBUG_CFLAGS = -MD -Od -Zi -Fd$(OBJDIR)/$(PDBFILE) # MSVC compiler options for release (optimized) builds # -MD - link with MSVCRT.LIB (Dynamically-linked, multi-threaded, C-runtime) # -O2 - Optimize for speed # -G5 - Optimize for Pentium WIN_OPT_CFLAGS = -MD -O2 ifdef BUILD_OPT OPTIMIZER = $(WIN_OPT_CFLAGS) else ifdef BUILD_IDG OPTIMIZER = $(WIN_IDG_CFLAGS) else OPTIMIZER = $(WIN_DEBUG_CFLAGS) endif endif OS_CFLAGS = -D_X86_=1 -DXP_WIN -DXP_WIN32 -DWIN32 -D_WINDOWS -D_WIN32 $(WIN_CFLAGS) JSDLL_CFLAGS = -DEXPORT_JS_API OS_LIBS = -lm -lc PREBUILT_CPUCFG = 1 USE_MSVC = 1 LIB_LINK_FLAGS=kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib\ advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib oldnames.lib \ winmm.lib \ -nologo\ -subsystem:windows -dll -debug -pdb:$(OBJDIR)/$(PDBFILE)\ -machine:I386\ -opt:ref -opt:noicf EXE_LINK_FLAGS=kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib\ advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib oldnames.lib -nologo\ -subsystem:console -debug -pdb:$(OBJDIR)/$(PDBFILE)\ -machine:I386\ -opt:ref -opt:noicf # CAFEDIR = t:/cafe # JCLASSPATH = $(CAFEDIR)/Java/Lib/classes.zip # JAVAC = $(CAFEDIR)/Bin/sj.exe # JAVAH = $(CAFEDIR)/Java/Bin/javah.exe # JCFLAGS = -I$(CAFEDIR)/Java/Include -I$(CAFEDIR)/Java/Include/win32 pacparser-1.4.5/src/spidermonkey/js/src/config/WINNT5.0.mk000066400000000000000000000077561464010763600231520ustar00rootroot00000000000000# -*- Mode: makefile -*- # # ***** BEGIN LICENSE BLOCK ***** # Version: MPL 1.1/GPL 2.0/LGPL 2.1 # # The contents of this file are subject to the Mozilla Public License Version # 1.1 (the "License"); you may not use this file except in compliance with # the License. You may obtain a copy of the License at # http://www.mozilla.org/MPL/ # # Software distributed under the License is distributed on an "AS IS" basis, # WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License # for the specific language governing rights and limitations under the # License. # # The Original Code is Mozilla Communicator client code, released # March 31, 1998. # # The Initial Developer of the Original Code is # Netscape Communications Corporation. # Portions created by the Initial Developer are Copyright (C) 1998 # the Initial Developer. All Rights Reserved. # # Contributor(s): # # Alternatively, the contents of this file may be used under the terms of # either the GNU General Public License Version 2 or later (the "GPL"), or # the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), # in which case the provisions of the GPL or the LGPL are applicable instead # of those above. If you wish to allow use of your version of this file only # under the terms of either the GPL or the LGPL, and not to allow others to # use your version of this file under the terms of the MPL, indicate your # decision by deleting the provisions above and replace them with the notice # and other provisions required by the GPL or the LGPL. If you do not delete # the provisions above, a recipient may use your version of this file under # the terms of any one of the MPL, the GPL or the LGPL. # # ***** END LICENSE BLOCK ***** # # Config for Windows NT using MS Visual C++ (version?) # CC = cl RANLIB = echo PDBFILE = $(basename $(@F)).pdb #.c.o: # $(CC) -c -MD $*.d $(CFLAGS) $< CPU_ARCH = x86 # XXX fixme GFX_ARCH = win32 # MSVC compiler options for both debug/optimize # -nologo - suppress copyright message # -W3 - Warning level 3 # -Gm - enable minimal rebuild # -Z7 - put debug info into the executable, not in .pdb file # -Zi - put debug info into .pdb file # -YX - automatic precompiled headers # -GX - enable C++ exception support WIN_CFLAGS = -nologo -W3 # MSVC compiler options for debug builds linked to MSVCRTD.DLL # -MDd - link with MSVCRTD.LIB (Dynamically-linked, multi-threaded, debug C-runtime) # -Od - minimal optimization WIN_IDG_CFLAGS = -MDd -Od -Z7 # MSVC compiler options for debug builds linked to MSVCRT.DLL # -MD - link with MSVCRT.LIB (Dynamically-linked, multi-threaded, debug C-runtime) # -Od - minimal optimization WIN_DEBUG_CFLAGS = -MD -Od -Zi -Fd$(OBJDIR)/$(PDBFILE) # MSVC compiler options for release (optimized) builds # -MD - link with MSVCRT.LIB (Dynamically-linked, multi-threaded, C-runtime) # -O2 - Optimize for speed # -G5 - Optimize for Pentium WIN_OPT_CFLAGS = -MD -O2 ifdef BUILD_OPT OPTIMIZER = $(WIN_OPT_CFLAGS) else ifdef BUILD_IDG OPTIMIZER = $(WIN_IDG_CFLAGS) else OPTIMIZER = $(WIN_DEBUG_CFLAGS) endif endif OS_CFLAGS = -D_X86_=1 -DXP_WIN -DXP_WIN32 -DWIN32 -D_WINDOWS -D_WIN32 -DWINVER=0x500 -D_WIN32_WINNT=0x500 $(WIN_CFLAGS) JSDLL_CFLAGS = -DEXPORT_JS_API OS_LIBS = -lm -lc PREBUILT_CPUCFG = 1 USE_MSVC = 1 LIB_LINK_FLAGS=kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib\ advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib oldnames.lib \ winmm.lib \ -nologo\ -subsystem:windows -dll -debug -pdb:$(OBJDIR)/$(PDBFILE)\ -machine:I386\ -opt:ref -opt:noicf EXE_LINK_FLAGS=kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib\ advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib oldnames.lib -nologo\ -subsystem:console -debug -pdb:$(OBJDIR)/$(PDBFILE)\ -machine:I386\ -opt:ref -opt:noicf # CAFEDIR = t:/cafe # JCLASSPATH = $(CAFEDIR)/Java/Lib/classes.zip # JAVAC = $(CAFEDIR)/Bin/sj.exe # JAVAH = $(CAFEDIR)/Java/Bin/javah.exe # JCFLAGS = -I$(CAFEDIR)/Java/Include -I$(CAFEDIR)/Java/Include/win32 pacparser-1.4.5/src/spidermonkey/js/src/config/WINNT5.1.mk000066400000000000000000000077561464010763600231530ustar00rootroot00000000000000# -*- Mode: makefile -*- # # ***** BEGIN LICENSE BLOCK ***** # Version: MPL 1.1/GPL 2.0/LGPL 2.1 # # The contents of this file are subject to the Mozilla Public License Version # 1.1 (the "License"); you may not use this file except in compliance with # the License. You may obtain a copy of the License at # http://www.mozilla.org/MPL/ # # Software distributed under the License is distributed on an "AS IS" basis, # WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License # for the specific language governing rights and limitations under the # License. # # The Original Code is Mozilla Communicator client code, released # March 31, 1998. # # The Initial Developer of the Original Code is # Netscape Communications Corporation. # Portions created by the Initial Developer are Copyright (C) 1998 # the Initial Developer. All Rights Reserved. # # Contributor(s): # # Alternatively, the contents of this file may be used under the terms of # either the GNU General Public License Version 2 or later (the "GPL"), or # the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), # in which case the provisions of the GPL or the LGPL are applicable instead # of those above. If you wish to allow use of your version of this file only # under the terms of either the GPL or the LGPL, and not to allow others to # use your version of this file under the terms of the MPL, indicate your # decision by deleting the provisions above and replace them with the notice # and other provisions required by the GPL or the LGPL. If you do not delete # the provisions above, a recipient may use your version of this file under # the terms of any one of the MPL, the GPL or the LGPL. # # ***** END LICENSE BLOCK ***** # # Config for Windows NT using MS Visual C++ (version?) # CC = cl RANLIB = echo PDBFILE = $(basename $(@F)).pdb #.c.o: # $(CC) -c -MD $*.d $(CFLAGS) $< CPU_ARCH = x86 # XXX fixme GFX_ARCH = win32 # MSVC compiler options for both debug/optimize # -nologo - suppress copyright message # -W3 - Warning level 3 # -Gm - enable minimal rebuild # -Z7 - put debug info into the executable, not in .pdb file # -Zi - put debug info into .pdb file # -YX - automatic precompiled headers # -GX - enable C++ exception support WIN_CFLAGS = -nologo -W3 # MSVC compiler options for debug builds linked to MSVCRTD.DLL # -MDd - link with MSVCRTD.LIB (Dynamically-linked, multi-threaded, debug C-runtime) # -Od - minimal optimization WIN_IDG_CFLAGS = -MDd -Od -Z7 # MSVC compiler options for debug builds linked to MSVCRT.DLL # -MD - link with MSVCRT.LIB (Dynamically-linked, multi-threaded, debug C-runtime) # -Od - minimal optimization WIN_DEBUG_CFLAGS = -MD -Od -Zi -Fd$(OBJDIR)/$(PDBFILE) # MSVC compiler options for release (optimized) builds # -MD - link with MSVCRT.LIB (Dynamically-linked, multi-threaded, C-runtime) # -O2 - Optimize for speed # -G5 - Optimize for Pentium WIN_OPT_CFLAGS = -MD -O2 ifdef BUILD_OPT OPTIMIZER = $(WIN_OPT_CFLAGS) else ifdef BUILD_IDG OPTIMIZER = $(WIN_IDG_CFLAGS) else OPTIMIZER = $(WIN_DEBUG_CFLAGS) endif endif OS_CFLAGS = -D_X86_=1 -DXP_WIN -DXP_WIN32 -DWIN32 -D_WINDOWS -D_WIN32 -DWINVER=0x500 -D_WIN32_WINNT=0x500 $(WIN_CFLAGS) JSDLL_CFLAGS = -DEXPORT_JS_API OS_LIBS = -lm -lc PREBUILT_CPUCFG = 1 USE_MSVC = 1 LIB_LINK_FLAGS=kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib\ advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib oldnames.lib \ winmm.lib \ -nologo\ -subsystem:windows -dll -debug -pdb:$(OBJDIR)/$(PDBFILE)\ -machine:I386\ -opt:ref -opt:noicf EXE_LINK_FLAGS=kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib\ advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib oldnames.lib -nologo\ -subsystem:console -debug -pdb:$(OBJDIR)/$(PDBFILE)\ -machine:I386\ -opt:ref -opt:noicf # CAFEDIR = t:/cafe # JCLASSPATH = $(CAFEDIR)/Java/Lib/classes.zip # JAVAC = $(CAFEDIR)/Bin/sj.exe # JAVAH = $(CAFEDIR)/Java/Bin/javah.exe # JCFLAGS = -I$(CAFEDIR)/Java/Include -I$(CAFEDIR)/Java/Include/win32 pacparser-1.4.5/src/spidermonkey/js/src/config/WINNT5.2.mk000066400000000000000000000077561464010763600231540ustar00rootroot00000000000000# -*- Mode: makefile -*- # # ***** BEGIN LICENSE BLOCK ***** # Version: MPL 1.1/GPL 2.0/LGPL 2.1 # # The contents of this file are subject to the Mozilla Public License Version # 1.1 (the "License"); you may not use this file except in compliance with # the License. You may obtain a copy of the License at # http://www.mozilla.org/MPL/ # # Software distributed under the License is distributed on an "AS IS" basis, # WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License # for the specific language governing rights and limitations under the # License. # # The Original Code is Mozilla Communicator client code, released # March 31, 1998. # # The Initial Developer of the Original Code is # Netscape Communications Corporation. # Portions created by the Initial Developer are Copyright (C) 1998 # the Initial Developer. All Rights Reserved. # # Contributor(s): # # Alternatively, the contents of this file may be used under the terms of # either the GNU General Public License Version 2 or later (the "GPL"), or # the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), # in which case the provisions of the GPL or the LGPL are applicable instead # of those above. If you wish to allow use of your version of this file only # under the terms of either the GPL or the LGPL, and not to allow others to # use your version of this file under the terms of the MPL, indicate your # decision by deleting the provisions above and replace them with the notice # and other provisions required by the GPL or the LGPL. If you do not delete # the provisions above, a recipient may use your version of this file under # the terms of any one of the MPL, the GPL or the LGPL. # # ***** END LICENSE BLOCK ***** # # Config for Windows NT using MS Visual C++ (version?) # CC = cl RANLIB = echo PDBFILE = $(basename $(@F)).pdb #.c.o: # $(CC) -c -MD $*.d $(CFLAGS) $< CPU_ARCH = x86 # XXX fixme GFX_ARCH = win32 # MSVC compiler options for both debug/optimize # -nologo - suppress copyright message # -W3 - Warning level 3 # -Gm - enable minimal rebuild # -Z7 - put debug info into the executable, not in .pdb file # -Zi - put debug info into .pdb file # -YX - automatic precompiled headers # -GX - enable C++ exception support WIN_CFLAGS = -nologo -W3 # MSVC compiler options for debug builds linked to MSVCRTD.DLL # -MDd - link with MSVCRTD.LIB (Dynamically-linked, multi-threaded, debug C-runtime) # -Od - minimal optimization WIN_IDG_CFLAGS = -MDd -Od -Z7 # MSVC compiler options for debug builds linked to MSVCRT.DLL # -MD - link with MSVCRT.LIB (Dynamically-linked, multi-threaded, debug C-runtime) # -Od - minimal optimization WIN_DEBUG_CFLAGS = -MD -Od -Zi -Fd$(OBJDIR)/$(PDBFILE) # MSVC compiler options for release (optimized) builds # -MD - link with MSVCRT.LIB (Dynamically-linked, multi-threaded, C-runtime) # -O2 - Optimize for speed # -G5 - Optimize for Pentium WIN_OPT_CFLAGS = -MD -O2 ifdef BUILD_OPT OPTIMIZER = $(WIN_OPT_CFLAGS) else ifdef BUILD_IDG OPTIMIZER = $(WIN_IDG_CFLAGS) else OPTIMIZER = $(WIN_DEBUG_CFLAGS) endif endif OS_CFLAGS = -D_X86_=1 -DXP_WIN -DXP_WIN32 -DWIN32 -D_WINDOWS -D_WIN32 -DWINVER=0x500 -D_WIN32_WINNT=0x500 $(WIN_CFLAGS) JSDLL_CFLAGS = -DEXPORT_JS_API OS_LIBS = -lm -lc PREBUILT_CPUCFG = 1 USE_MSVC = 1 LIB_LINK_FLAGS=kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib\ advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib oldnames.lib \ winmm.lib \ -nologo\ -subsystem:windows -dll -debug -pdb:$(OBJDIR)/$(PDBFILE)\ -machine:I386\ -opt:ref -opt:noicf EXE_LINK_FLAGS=kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib\ advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib oldnames.lib -nologo\ -subsystem:console -debug -pdb:$(OBJDIR)/$(PDBFILE)\ -machine:I386\ -opt:ref -opt:noicf # CAFEDIR = t:/cafe # JCLASSPATH = $(CAFEDIR)/Java/Lib/classes.zip # JAVAC = $(CAFEDIR)/Bin/sj.exe # JAVAH = $(CAFEDIR)/Java/Bin/javah.exe # JCFLAGS = -I$(CAFEDIR)/Java/Include -I$(CAFEDIR)/Java/Include/win32 pacparser-1.4.5/src/spidermonkey/js/src/config/dgux.mk000066400000000000000000000043441464010763600227650ustar00rootroot00000000000000# -*- Mode: makefile -*- # # ***** BEGIN LICENSE BLOCK ***** # Version: MPL 1.1/GPL 2.0/LGPL 2.1 # # The contents of this file are subject to the Mozilla Public License Version # 1.1 (the "License"); you may not use this file except in compliance with # the License. You may obtain a copy of the License at # http://www.mozilla.org/MPL/ # # Software distributed under the License is distributed on an "AS IS" basis, # WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License # for the specific language governing rights and limitations under the # License. # # The Original Code is Mozilla Communicator client code, released # March 31, 1998. # # The Initial Developer of the Original Code is # Netscape Communications Corporation. # Portions created by the Initial Developer are Copyright (C) 1998 # the Initial Developer. All Rights Reserved. # # Contributor(s): # # Alternatively, the contents of this file may be used under the terms of # either the GNU General Public License Version 2 or later (the "GPL"), or # the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), # in which case the provisions of the GPL or the LGPL are applicable instead # of those above. If you wish to allow use of your version of this file only # under the terms of either the GPL or the LGPL, and not to allow others to # use your version of this file under the terms of the MPL, indicate your # decision by deleting the provisions above and replace them with the notice # and other provisions required by the GPL or the LGPL. If you do not delete # the provisions above, a recipient may use your version of this file under # the terms of any one of the MPL, the GPL or the LGPL. # # ***** END LICENSE BLOCK ***** # # Config stuff for Data General DG/UX # # # Initial DG/UX port by Marc Fraioli (fraioli@dg-rtp.dg.com) # AS = as CC = gcc CCC = g++ RANLIB = echo # # _DGUX_SOURCE is needed to turn on a lot of stuff in the headers if # you're not using DG's compiler. It shouldn't hurt if you are. # # _POSIX4A_DRAFT10_SOURCE is needed to pick up localtime_r, used in # prtime.c # OS_CFLAGS = -DXP_UNIX -DSVR4 -DSYSV -DDGUX -D_DGUX_SOURCE -D_POSIX4A_DRAFT10_SOURCE -DHAVE_LOCALTIME_R OS_LIBS = -lsocket -lnsl NOSUCHFILE = /no-such-file pacparser-1.4.5/src/spidermonkey/js/src/js.c000066400000000000000000002543741464010763600210120ustar00rootroot00000000000000/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- * vim: set ts=8 sw=4 et tw=78: * * ***** BEGIN LICENSE BLOCK ***** * Version: MPL 1.1/GPL 2.0/LGPL 2.1 * * The contents of this file are subject to the Mozilla Public License Version * 1.1 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * http://www.mozilla.org/MPL/ * * Software distributed under the License is distributed on an "AS IS" basis, * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License * for the specific language governing rights and limitations under the * License. * * The Original Code is Mozilla Communicator client code, released * March 31, 1998. * * The Initial Developer of the Original Code is * Netscape Communications Corporation. * Portions created by the Initial Developer are Copyright (C) 1998 * the Initial Developer. All Rights Reserved. * * Contributor(s): * * Alternatively, the contents of this file may be used under the terms of * either of the GNU General Public License Version 2 or later (the "GPL"), * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), * in which case the provisions of the GPL or the LGPL are applicable instead * of those above. If you wish to allow use of your version of this file only * under the terms of either the GPL or the LGPL, and not to allow others to * use your version of this file under the terms of the MPL, indicate your * decision by deleting the provisions above and replace them with the notice * and other provisions required by the GPL or the LGPL. If you do not delete * the provisions above, a recipient may use your version of this file under * the terms of any one of the MPL, the GPL or the LGPL. * * ***** END LICENSE BLOCK ***** */ /* * JS shell. */ #include "jsstddef.h" #include #include #include #include #include #include "jstypes.h" #include "jsarena.h" #include "jsutil.h" #include "jsprf.h" #include "jsapi.h" #include "jsatom.h" #include "jscntxt.h" #include "jsdbgapi.h" #include "jsemit.h" #include "jsfun.h" #include "jsgc.h" #include "jslock.h" #include "jsobj.h" #include "jsparse.h" #include "jsscope.h" #include "jsscript.h" #ifdef PERLCONNECT #include "perlconnect/jsperl.h" #endif #ifdef LIVECONNECT #include "jsjava.h" #endif #ifdef JSDEBUGGER #include "jsdebug.h" #ifdef JSDEBUGGER_JAVA_UI #include "jsdjava.h" #endif /* JSDEBUGGER_JAVA_UI */ #ifdef JSDEBUGGER_C_UI #include "jsdb.h" #endif /* JSDEBUGGER_C_UI */ #endif /* JSDEBUGGER */ #ifdef XP_UNIX #include #include #include #endif #if defined(XP_WIN) || defined(XP_OS2) #include /* for isatty() */ #endif typedef enum JSShellExitCode { EXITCODE_RUNTIME_ERROR = 3, EXITCODE_FILE_NOT_FOUND = 4, EXITCODE_OUT_OF_MEMORY = 5 } JSShellExitCode; size_t gStackChunkSize = 8192; /* Assume that we can not use more than 5e5 bytes of C stack by default. */ static size_t gMaxStackSize = 500000; static jsuword gStackBase; int gExitCode = 0; JSBool gQuitting = JS_FALSE; FILE *gErrFile = NULL; FILE *gOutFile = NULL; #ifdef JSDEBUGGER static JSDContext *_jsdc; #ifdef JSDEBUGGER_JAVA_UI static JSDJContext *_jsdjc; #endif /* JSDEBUGGER_JAVA_UI */ #endif /* JSDEBUGGER */ static JSBool reportWarnings = JS_TRUE; static JSBool compileOnly = JS_FALSE; typedef enum JSShellErrNum { #define MSG_DEF(name, number, count, exception, format) \ name = number, #include "jsshell.msg" #undef MSG_DEF JSShellErr_Limit #undef MSGDEF } JSShellErrNum; static const JSErrorFormatString * my_GetErrorMessage(void *userRef, const char *locale, const uintN errorNumber); static JSObject * split_setup(JSContext *cx); #ifdef EDITLINE extern char *readline(const char *prompt); extern void add_history(char *line); #endif static JSBool GetLine(JSContext *cx, char *bufp, FILE *file, const char *prompt) { #ifdef EDITLINE /* * Use readline only if file is stdin, because there's no way to specify * another handle. Are other filehandles interactive? */ if (file == stdin) { char *linep = readline(prompt); if (!linep) return JS_FALSE; if (linep[0] != '\0') add_history(linep); strcpy(bufp, linep); JS_free(cx, linep); bufp += strlen(bufp); *bufp++ = '\n'; *bufp = '\0'; } else #endif { char line[256]; fprintf(gOutFile, prompt); fflush(gOutFile); if (!fgets(line, sizeof line, file)) return JS_FALSE; strcpy(bufp, line); } return JS_TRUE; } static void Process(JSContext *cx, JSObject *obj, char *filename, JSBool forceTTY) { JSBool ok, hitEOF; JSScript *script; jsval result; JSString *str; char buffer[4096]; char *bufp; int lineno; int startline; FILE *file; jsuword stackLimit; if (forceTTY || !filename || strcmp(filename, "-") == 0) { file = stdin; } else { file = fopen(filename, "r"); if (!file) { JS_ReportErrorNumber(cx, my_GetErrorMessage, NULL, JSSMSG_CANT_OPEN, filename, strerror(errno)); gExitCode = EXITCODE_FILE_NOT_FOUND; return; } } if (gMaxStackSize == 0) { /* * Disable checking for stack overflow if limit is zero. */ stackLimit = 0; } else { #if JS_STACK_GROWTH_DIRECTION > 0 stackLimit = gStackBase + gMaxStackSize; #else stackLimit = gStackBase - gMaxStackSize; #endif } JS_SetThreadStackLimit(cx, stackLimit); if (!forceTTY && !isatty(fileno(file))) { /* * It's not interactive - just execute it. * * Support the UNIX #! shell hack; gobble the first line if it starts * with '#'. TODO - this isn't quite compatible with sharp variables, * as a legal js program (using sharp variables) might start with '#'. * But that would require multi-character lookahead. */ int ch = fgetc(file); if (ch == '#') { while((ch = fgetc(file)) != EOF) { if (ch == '\n' || ch == '\r') break; } } ungetc(ch, file); script = JS_CompileFileHandle(cx, obj, filename, file); if (script) { if (!compileOnly) (void)JS_ExecuteScript(cx, obj, script, &result); JS_DestroyScript(cx, script); } return; } /* It's an interactive filehandle; drop into read-eval-print loop. */ lineno = 1; hitEOF = JS_FALSE; do { bufp = buffer; *bufp = '\0'; /* * Accumulate lines until we get a 'compilable unit' - one that either * generates an error (before running out of source) or that compiles * cleanly. This should be whenever we get a complete statement that * coincides with the end of a line. */ startline = lineno; do { if (!GetLine(cx, bufp, file, startline == lineno ? "js> " : "")) { hitEOF = JS_TRUE; break; } bufp += strlen(bufp); lineno++; } while (!JS_BufferIsCompilableUnit(cx, obj, buffer, strlen(buffer))); /* Clear any pending exception from previous failed compiles. */ JS_ClearPendingException(cx); script = JS_CompileScript(cx, obj, buffer, strlen(buffer), "typein", startline); if (script) { if (!compileOnly) { ok = JS_ExecuteScript(cx, obj, script, &result); if (ok && result != JSVAL_VOID) { str = JS_ValueToString(cx, result); if (str) fprintf(gOutFile, "%s\n", JS_GetStringBytes(str)); else ok = JS_FALSE; } } JS_DestroyScript(cx, script); } } while (!hitEOF && !gQuitting); fprintf(gOutFile, "\n"); return; } static int usage(void) { fprintf(gErrFile, "%s\n", JS_GetImplementationVersion()); fprintf(gErrFile, "usage: js [-PswWxCi] [-b branchlimit] [-c stackchunksize] [-v version] [-f scriptfile] [-e script] [-S maxstacksize] [scriptfile] [scriptarg...]\n"); return 2; } static uint32 gBranchCount; static uint32 gBranchLimit; static JSBool my_BranchCallback(JSContext *cx, JSScript *script) { if (++gBranchCount == gBranchLimit) { if (script) { if (script->filename) fprintf(gErrFile, "%s:", script->filename); fprintf(gErrFile, "%u: script branch callback (%u callbacks)\n", script->lineno, gBranchLimit); } else { fprintf(gErrFile, "native branch callback (%u callbacks)\n", gBranchLimit); } gBranchCount = 0; return JS_FALSE; } if ((gBranchCount & 0x3fff) == 1) JS_MaybeGC(cx); return JS_TRUE; } extern JSClass global_class; static int ProcessArgs(JSContext *cx, JSObject *obj, char **argv, int argc) { int i, j, length; JSObject *argsObj; char *filename = NULL; JSBool isInteractive = JS_TRUE; JSBool forceTTY = JS_FALSE; /* * Scan past all optional arguments so we can create the arguments object * before processing any -f options, which must interleave properly with * -v and -w options. This requires two passes, and without getopt, we'll * have to keep the option logic here and in the second for loop in sync. */ for (i = 0; i < argc; i++) { if (argv[i][0] != '-' || argv[i][1] == '\0') { ++i; break; } switch (argv[i][1]) { case 'b': case 'c': case 'f': case 'e': case 'v': case 'S': ++i; break; default:; } } /* * Create arguments early and define it to root it, so it's safe from any * GC calls nested below, and so it is available to -f arguments. */ argsObj = JS_NewArrayObject(cx, 0, NULL); if (!argsObj) return 1; if (!JS_DefineProperty(cx, obj, "arguments", OBJECT_TO_JSVAL(argsObj), NULL, NULL, 0)) { return 1; } length = argc - i; for (j = 0; j < length; j++) { JSString *str = JS_NewStringCopyZ(cx, argv[i++]); if (!str) return 1; if (!JS_DefineElement(cx, argsObj, j, STRING_TO_JSVAL(str), NULL, NULL, JSPROP_ENUMERATE)) { return 1; } } for (i = 0; i < argc; i++) { if (argv[i][0] != '-' || argv[i][1] == '\0') { filename = argv[i++]; isInteractive = JS_FALSE; break; } switch (argv[i][1]) { case 'v': if (++i == argc) return usage(); JS_SetVersion(cx, (JSVersion) atoi(argv[i])); break; case 'w': reportWarnings = JS_TRUE; break; case 'W': reportWarnings = JS_FALSE; break; case 's': JS_ToggleOptions(cx, JSOPTION_STRICT); break; case 'x': JS_ToggleOptions(cx, JSOPTION_XML); break; case 'P': if (JS_GET_CLASS(cx, JS_GetPrototype(cx, obj)) != &global_class) { JSObject *gobj; if (!JS_SealObject(cx, obj, JS_TRUE)) return JS_FALSE; gobj = JS_NewObject(cx, &global_class, NULL, NULL); if (!gobj) return JS_FALSE; if (!JS_SetPrototype(cx, gobj, obj)) return JS_FALSE; JS_SetParent(cx, gobj, NULL); JS_SetGlobalObject(cx, gobj); obj = gobj; } break; case 'b': gBranchLimit = atoi(argv[++i]); JS_SetBranchCallback(cx, my_BranchCallback); JS_ToggleOptions(cx, JSOPTION_NATIVE_BRANCH_CALLBACK); break; case 'c': /* set stack chunk size */ gStackChunkSize = atoi(argv[++i]); break; case 'f': if (++i == argc) return usage(); Process(cx, obj, argv[i], JS_FALSE); /* * XXX: js -f foo.js should interpret foo.js and then * drop into interactive mode, but that breaks the test * harness. Just execute foo.js for now. */ isInteractive = JS_FALSE; break; case 'e': { jsval rval; if (++i == argc) return usage(); /* Pass a filename of -e to imitate PERL */ JS_EvaluateScript(cx, obj, argv[i], strlen(argv[i]), "-e", 1, &rval); isInteractive = JS_FALSE; break; } case 'C': compileOnly = JS_TRUE; isInteractive = JS_FALSE; break; case 'i': isInteractive = forceTTY = JS_TRUE; break; case 'S': if (++i == argc) return usage(); /* Set maximum stack size. */ gMaxStackSize = atoi(argv[i]); break; case 'z': obj = split_setup(cx); break; default: return usage(); } } if (filename || isInteractive) Process(cx, obj, filename, forceTTY); return gExitCode; } static JSBool Version(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) { if (argc > 0 && JSVAL_IS_INT(argv[0])) *rval = INT_TO_JSVAL(JS_SetVersion(cx, (JSVersion) JSVAL_TO_INT(argv[0]))); else *rval = INT_TO_JSVAL(JS_GetVersion(cx)); return JS_TRUE; } static struct { const char *name; uint32 flag; } js_options[] = { {"strict", JSOPTION_STRICT}, {"werror", JSOPTION_WERROR}, {"atline", JSOPTION_ATLINE}, {"xml", JSOPTION_XML}, {0, 0} }; static JSBool Options(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) { uint32 optset, flag; uintN i, j, found; JSString *str; const char *opt; char *names; optset = 0; for (i = 0; i < argc; i++) { str = JS_ValueToString(cx, argv[i]); if (!str) return JS_FALSE; opt = JS_GetStringBytes(str); for (j = 0; js_options[j].name; j++) { if (strcmp(js_options[j].name, opt) == 0) { optset |= js_options[j].flag; break; } } } optset = JS_ToggleOptions(cx, optset); names = NULL; found = 0; while (optset != 0) { flag = optset; optset &= optset - 1; flag &= ~optset; for (j = 0; js_options[j].name; j++) { if (js_options[j].flag == flag) { names = JS_sprintf_append(names, "%s%s", names ? "," : "", js_options[j].name); found++; break; } } } if (!found) names = strdup(""); if (!names) { JS_ReportOutOfMemory(cx); return JS_FALSE; } str = JS_NewString(cx, names, strlen(names)); if (!str) { free(names); return JS_FALSE; } *rval = STRING_TO_JSVAL(str); return JS_TRUE; } static JSBool Load(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) { uintN i; JSString *str; const char *filename; JSScript *script; JSBool ok; jsval result; uint32 oldopts; for (i = 0; i < argc; i++) { str = JS_ValueToString(cx, argv[i]); if (!str) return JS_FALSE; argv[i] = STRING_TO_JSVAL(str); filename = JS_GetStringBytes(str); errno = 0; oldopts = JS_GetOptions(cx); JS_SetOptions(cx, oldopts | JSOPTION_COMPILE_N_GO); script = JS_CompileFile(cx, obj, filename); if (!script) { ok = JS_FALSE; } else { ok = !compileOnly ? JS_ExecuteScript(cx, obj, script, &result) : JS_TRUE; JS_DestroyScript(cx, script); } JS_SetOptions(cx, oldopts); if (!ok) return JS_FALSE; } return JS_TRUE; } /* * function readline() * Provides a hook for scripts to read a line from stdin. */ static JSBool ReadLine(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) { #define BUFSIZE 256 FILE *from; char *buf, *tmp; size_t bufsize, buflength, gotlength; JSString *str; from = stdin; buflength = 0; bufsize = BUFSIZE; buf = JS_malloc(cx, bufsize); if (!buf) return JS_FALSE; while ((gotlength = js_fgets(buf + buflength, bufsize - buflength, from)) > 0) { buflength += gotlength; /* Are we done? */ if (buf[buflength - 1] == '\n') { buf[buflength - 1] = '\0'; break; } /* Else, grow our buffer for another pass. */ tmp = JS_realloc(cx, buf, bufsize * 2); if (!tmp) { JS_free(cx, buf); return JS_FALSE; } bufsize *= 2; buf = tmp; } /* Treat the empty string specially. */ if (buflength == 0) { *rval = JS_GetEmptyStringValue(cx); JS_free(cx, buf); return JS_TRUE; } /* Shrink the buffer to the real size. */ tmp = JS_realloc(cx, buf, buflength); if (!tmp) { JS_free(cx, buf); return JS_FALSE; } buf = tmp; /* * Turn buf into a JSString. Note that buflength includes the trailing null * character. */ str = JS_NewString(cx, buf, buflength - 1); if (!str) { JS_free(cx, buf); return JS_FALSE; } *rval = STRING_TO_JSVAL(str); return JS_TRUE; } static JSBool Print(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) { uintN i, n; JSString *str; for (i = n = 0; i < argc; i++) { str = JS_ValueToString(cx, argv[i]); if (!str) return JS_FALSE; fprintf(gOutFile, "%s%s", i ? " " : "", JS_GetStringBytes(str)); } n++; if (n) fputc('\n', gOutFile); return JS_TRUE; } static JSBool Help(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval); static JSBool Quit(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) { #ifdef LIVECONNECT JSJ_SimpleShutdown(); #endif JS_ConvertArguments(cx, argc, argv,"/ i", &gExitCode); gQuitting = JS_TRUE; return JS_FALSE; } static JSBool GC(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) { JSRuntime *rt; uint32 preBytes; rt = cx->runtime; preBytes = rt->gcBytes; #ifdef GC_MARK_DEBUG if (argc && JSVAL_IS_STRING(argv[0])) { char *name = JS_GetStringBytes(JSVAL_TO_STRING(argv[0])); FILE *file = fopen(name, "w"); if (!file) { fprintf(gErrFile, "gc: can't open %s: %s\n", strerror(errno)); return JS_FALSE; } js_DumpGCHeap = file; } else { js_DumpGCHeap = stdout; } #endif JS_GC(cx); #ifdef GC_MARK_DEBUG if (js_DumpGCHeap != stdout) fclose(js_DumpGCHeap); js_DumpGCHeap = NULL; #endif fprintf(gOutFile, "before %lu, after %lu, break %08lx\n", (unsigned long)preBytes, (unsigned long)rt->gcBytes, #ifdef XP_UNIX (unsigned long)sbrk(0) #else 0 #endif ); #ifdef JS_GCMETER js_DumpGCStats(rt, stdout); #endif return JS_TRUE; } static JSScript * ValueToScript(JSContext *cx, jsval v) { JSScript *script; JSFunction *fun; if (!JSVAL_IS_PRIMITIVE(v) && JS_GET_CLASS(cx, JSVAL_TO_OBJECT(v)) == &js_ScriptClass) { script = (JSScript *) JS_GetPrivate(cx, JSVAL_TO_OBJECT(v)); } else { fun = JS_ValueToFunction(cx, v); if (!fun) return NULL; script = FUN_SCRIPT(fun); } return script; } static JSBool GetTrapArgs(JSContext *cx, uintN argc, jsval *argv, JSScript **scriptp, int32 *ip) { jsval v; uintN intarg; JSScript *script; *scriptp = cx->fp->down->script; *ip = 0; if (argc != 0) { v = argv[0]; intarg = 0; if (!JSVAL_IS_PRIMITIVE(v) && (JS_GET_CLASS(cx, JSVAL_TO_OBJECT(v)) == &js_FunctionClass || JS_GET_CLASS(cx, JSVAL_TO_OBJECT(v)) == &js_ScriptClass)) { script = ValueToScript(cx, v); if (!script) return JS_FALSE; *scriptp = script; intarg++; } if (argc > intarg) { if (!JS_ValueToInt32(cx, argv[intarg], ip)) return JS_FALSE; } } return JS_TRUE; } static JSTrapStatus TrapHandler(JSContext *cx, JSScript *script, jsbytecode *pc, jsval *rval, void *closure) { JSString *str; JSStackFrame *caller; str = (JSString *) closure; caller = JS_GetScriptedCaller(cx, NULL); if (!JS_EvaluateScript(cx, caller->scopeChain, JS_GetStringBytes(str), JS_GetStringLength(str), caller->script->filename, caller->script->lineno, rval)) { return JSTRAP_ERROR; } if (*rval != JSVAL_VOID) return JSTRAP_RETURN; return JSTRAP_CONTINUE; } static JSBool Trap(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) { JSString *str; JSScript *script; int32 i; if (argc == 0) { JS_ReportErrorNumber(cx, my_GetErrorMessage, NULL, JSSMSG_TRAP_USAGE); return JS_FALSE; } argc--; str = JS_ValueToString(cx, argv[argc]); if (!str) return JS_FALSE; argv[argc] = STRING_TO_JSVAL(str); if (!GetTrapArgs(cx, argc, argv, &script, &i)) return JS_FALSE; return JS_SetTrap(cx, script, script->code + i, TrapHandler, str); } static JSBool Untrap(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) { JSScript *script; int32 i; if (!GetTrapArgs(cx, argc, argv, &script, &i)) return JS_FALSE; JS_ClearTrap(cx, script, script->code + i, NULL, NULL); return JS_TRUE; } static JSBool LineToPC(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) { JSScript *script; int32 i; uintN lineno; jsbytecode *pc; if (argc == 0) { JS_ReportErrorNumber(cx, my_GetErrorMessage, NULL, JSSMSG_LINE2PC_USAGE); return JS_FALSE; } script = cx->fp->down->script; if (!GetTrapArgs(cx, argc, argv, &script, &i)) return JS_FALSE; lineno = (i == 0) ? script->lineno : (uintN)i; pc = JS_LineNumberToPC(cx, script, lineno); if (!pc) return JS_FALSE; *rval = INT_TO_JSVAL(PTRDIFF(pc, script->code, jsbytecode)); return JS_TRUE; } static JSBool PCToLine(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) { JSScript *script; int32 i; uintN lineno; if (!GetTrapArgs(cx, argc, argv, &script, &i)) return JS_FALSE; lineno = JS_PCToLineNumber(cx, script, script->code + i); if (!lineno) return JS_FALSE; *rval = INT_TO_JSVAL(lineno); return JS_TRUE; } #ifdef DEBUG static void GetSwitchTableBounds(JSScript *script, uintN offset, uintN *start, uintN *end) { jsbytecode *pc; JSOp op; ptrdiff_t jmplen; jsint low, high, n; pc = script->code + offset; op = *pc; switch (op) { case JSOP_TABLESWITCHX: jmplen = JUMPX_OFFSET_LEN; goto jump_table; case JSOP_TABLESWITCH: jmplen = JUMP_OFFSET_LEN; jump_table: pc += jmplen; low = GET_JUMP_OFFSET(pc); pc += JUMP_OFFSET_LEN; high = GET_JUMP_OFFSET(pc); pc += JUMP_OFFSET_LEN; n = high - low + 1; break; case JSOP_LOOKUPSWITCHX: jmplen = JUMPX_OFFSET_LEN; goto lookup_table; default: JS_ASSERT(op == JSOP_LOOKUPSWITCH); jmplen = JUMP_OFFSET_LEN; lookup_table: pc += jmplen; n = GET_ATOM_INDEX(pc); pc += ATOM_INDEX_LEN; jmplen += ATOM_INDEX_LEN; break; } *start = (uintN)(pc - script->code); *end = *start + (uintN)(n * jmplen); } /* * SrcNotes assumes that SRC_METHODBASE should be distinguished from SRC_LABEL * using the bytecode the source note points to. */ JS_STATIC_ASSERT(SRC_LABEL == SRC_METHODBASE); static void SrcNotes(JSContext *cx, JSScript *script) { uintN offset, delta, caseOff, switchTableStart, switchTableEnd; jssrcnote *notes, *sn; JSSrcNoteType type; const char *name; JSOp op; jsatomid atomIndex; JSAtom *atom; fprintf(gOutFile, "\nSource notes:\n"); offset = 0; notes = SCRIPT_NOTES(script); switchTableEnd = switchTableStart = 0; for (sn = notes; !SN_IS_TERMINATOR(sn); sn = SN_NEXT(sn)) { delta = SN_DELTA(sn); offset += delta; type = (JSSrcNoteType) SN_TYPE(sn); name = js_SrcNoteSpec[type].name; if (type == SRC_LABEL) { /* Heavily overloaded case. */ if (switchTableStart <= offset && offset < switchTableEnd) { name = "case"; } else { op = script->code[offset]; if (op == JSOP_GETMETHOD || op == JSOP_SETMETHOD) { /* This is SRC_METHODBASE which we print as SRC_PCBASE. */ type = SRC_PCBASE; name = "methodbase"; } else { JS_ASSERT(op == JSOP_NOP); } } } fprintf(gOutFile, "%3u: %5u [%4u] %-8s", PTRDIFF(sn, notes, jssrcnote), offset, delta, name); switch (type) { case SRC_SETLINE: fprintf(gOutFile, " lineno %u", (uintN) js_GetSrcNoteOffset(sn, 0)); break; case SRC_FOR: fprintf(gOutFile, " cond %u update %u tail %u", (uintN) js_GetSrcNoteOffset(sn, 0), (uintN) js_GetSrcNoteOffset(sn, 1), (uintN) js_GetSrcNoteOffset(sn, 2)); break; case SRC_IF_ELSE: fprintf(gOutFile, " else %u elseif %u", (uintN) js_GetSrcNoteOffset(sn, 0), (uintN) js_GetSrcNoteOffset(sn, 1)); break; case SRC_COND: case SRC_WHILE: case SRC_PCBASE: case SRC_PCDELTA: case SRC_DECL: case SRC_BRACE: fprintf(gOutFile, " offset %u", (uintN) js_GetSrcNoteOffset(sn, 0)); break; case SRC_LABEL: case SRC_LABELBRACE: case SRC_BREAK2LABEL: case SRC_CONT2LABEL: case SRC_FUNCDEF: { const char *bytes; JSFunction *fun; JSString *str; atomIndex = (jsatomid) js_GetSrcNoteOffset(sn, 0); atom = js_GetAtom(cx, &script->atomMap, atomIndex); if (type != SRC_FUNCDEF) { bytes = js_AtomToPrintableString(cx, atom); } else { fun = (JSFunction *) JS_GetPrivate(cx, ATOM_TO_OBJECT(atom)); str = JS_DecompileFunction(cx, fun, JS_DONT_PRETTY_PRINT); bytes = str ? JS_GetStringBytes(str) : "N/A"; } fprintf(gOutFile, " atom %u (%s)", (uintN)atomIndex, bytes); break; } case SRC_SWITCH: fprintf(gOutFile, " length %u", (uintN) js_GetSrcNoteOffset(sn, 0)); caseOff = (uintN) js_GetSrcNoteOffset(sn, 1); if (caseOff) fprintf(gOutFile, " first case offset %u", caseOff); GetSwitchTableBounds(script, offset, &switchTableStart, &switchTableEnd); break; case SRC_CATCH: delta = (uintN) js_GetSrcNoteOffset(sn, 0); if (delta) { if (script->main[offset] == JSOP_LEAVEBLOCK) fprintf(gOutFile, " stack depth %u", delta); else fprintf(gOutFile, " guard delta %u", delta); } break; default:; } fputc('\n', gOutFile); } } static JSBool Notes(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) { uintN i; JSScript *script; for (i = 0; i < argc; i++) { script = ValueToScript(cx, argv[i]); if (!script) continue; SrcNotes(cx, script); } return JS_TRUE; } static JSBool TryNotes(JSContext *cx, JSScript *script) { JSTryNote *tn = script->trynotes; if (!tn) return JS_TRUE; fprintf(gOutFile, "\nException table:\nstart\tend\tcatch\n"); while (tn->start && tn->catchStart) { fprintf(gOutFile, " %d\t%d\t%d\n", tn->start, tn->start + tn->length, tn->catchStart); tn++; } return JS_TRUE; } static JSBool Disassemble(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) { JSBool lines; uintN i; JSScript *script; if (argc > 0 && JSVAL_IS_STRING(argv[0]) && !strcmp(JS_GetStringBytes(JSVAL_TO_STRING(argv[0])), "-l")) { lines = JS_TRUE; argv++, argc--; } else { lines = JS_FALSE; } for (i = 0; i < argc; i++) { script = ValueToScript(cx, argv[i]); if (!script) return JS_FALSE; if (VALUE_IS_FUNCTION(cx, argv[i])) { JSFunction *fun = JS_ValueToFunction(cx, argv[i]); if (fun && (fun->flags & JSFUN_FLAGS_MASK)) { uint16 flags = fun->flags; fputs("flags:", stdout); #define SHOW_FLAG(flag) if (flags & JSFUN_##flag) fputs(" " #flag, stdout); SHOW_FLAG(LAMBDA); SHOW_FLAG(SETTER); SHOW_FLAG(GETTER); SHOW_FLAG(BOUND_METHOD); SHOW_FLAG(HEAVYWEIGHT); SHOW_FLAG(THISP_STRING); SHOW_FLAG(THISP_NUMBER); SHOW_FLAG(THISP_BOOLEAN); SHOW_FLAG(INTERPRETED); #undef SHOW_FLAG putchar('\n'); } } if (!js_Disassemble(cx, script, lines, stdout)) return JS_FALSE; SrcNotes(cx, script); TryNotes(cx, script); } return JS_TRUE; } static JSBool DisassWithSrc(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) { #define LINE_BUF_LEN 512 uintN i, len, line1, line2, bupline; JSScript *script; FILE *file; char linebuf[LINE_BUF_LEN]; jsbytecode *pc, *end; static char sep[] = ";-------------------------"; for (i = 0; i < argc; i++) { script = ValueToScript(cx, argv[i]); if (!script) return JS_FALSE; if (!script || !script->filename) { JS_ReportErrorNumber(cx, my_GetErrorMessage, NULL, JSSMSG_FILE_SCRIPTS_ONLY); return JS_FALSE; } file = fopen(script->filename, "r"); if (!file) { JS_ReportErrorNumber(cx, my_GetErrorMessage, NULL, JSSMSG_CANT_OPEN, script->filename, strerror(errno)); return JS_FALSE; } pc = script->code; end = pc + script->length; /* burn the leading lines */ line2 = JS_PCToLineNumber(cx, script, pc); for (line1 = 0; line1 < line2 - 1; line1++) fgets(linebuf, LINE_BUF_LEN, file); bupline = 0; while (pc < end) { line2 = JS_PCToLineNumber(cx, script, pc); if (line2 < line1) { if (bupline != line2) { bupline = line2; fprintf(gOutFile, "%s %3u: BACKUP\n", sep, line2); } } else { if (bupline && line1 == line2) fprintf(gOutFile, "%s %3u: RESTORE\n", sep, line2); bupline = 0; while (line1 < line2) { if (!fgets(linebuf, LINE_BUF_LEN, file)) { JS_ReportErrorNumber(cx, my_GetErrorMessage, NULL, JSSMSG_UNEXPECTED_EOF, script->filename); goto bail; } line1++; fprintf(gOutFile, "%s %3u: %s", sep, line1, linebuf); } } len = js_Disassemble1(cx, script, pc, PTRDIFF(pc, script->code, jsbytecode), JS_TRUE, stdout); if (!len) return JS_FALSE; pc += len; } bail: fclose(file); } return JS_TRUE; #undef LINE_BUF_LEN } static JSBool Tracing(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) { JSBool bval; JSString *str; if (argc == 0) { *rval = BOOLEAN_TO_JSVAL(cx->tracefp != 0); return JS_TRUE; } switch (JS_TypeOfValue(cx, argv[0])) { case JSTYPE_NUMBER: bval = JSVAL_IS_INT(argv[0]) ? JSVAL_TO_INT(argv[0]) : (jsint) *JSVAL_TO_DOUBLE(argv[0]); break; case JSTYPE_BOOLEAN: bval = JSVAL_TO_BOOLEAN(argv[0]); break; default: str = JS_ValueToString(cx, argv[0]); if (!str) return JS_FALSE; fprintf(gErrFile, "tracing: illegal argument %s\n", JS_GetStringBytes(str)); return JS_TRUE; } cx->tracefp = bval ? stderr : NULL; return JS_TRUE; } typedef struct DumpAtomArgs { JSContext *cx; FILE *fp; } DumpAtomArgs; static int DumpAtom(JSHashEntry *he, int i, void *arg) { DumpAtomArgs *args = (DumpAtomArgs *)arg; FILE *fp = args->fp; JSAtom *atom = (JSAtom *)he; fprintf(fp, "%3d %08x %5lu ", i, (uintN)he->keyHash, (unsigned long)atom->number); if (ATOM_IS_STRING(atom)) fprintf(fp, "\"%s\"\n", js_AtomToPrintableString(args->cx, atom)); else if (ATOM_IS_INT(atom)) fprintf(fp, "%ld\n", (long)ATOM_TO_INT(atom)); else fprintf(fp, "%.16g\n", *ATOM_TO_DOUBLE(atom)); return HT_ENUMERATE_NEXT; } static void DumpScope(JSContext *cx, JSObject *obj, FILE *fp) { uintN i; JSScope *scope; JSScopeProperty *sprop; i = 0; scope = OBJ_SCOPE(obj); for (sprop = SCOPE_LAST_PROP(scope); sprop; sprop = sprop->parent) { if (SCOPE_HAD_MIDDLE_DELETE(scope) && !SCOPE_HAS_PROPERTY(scope, sprop)) continue; fprintf(fp, "%3u %p", i, (void *)sprop); if (JSID_IS_INT(sprop->id)) { fprintf(fp, " [%ld]", (long)JSVAL_TO_INT(sprop->id)); } else if (JSID_IS_ATOM(sprop->id)) { JSAtom *atom = JSID_TO_ATOM(sprop->id); fprintf(fp, " \"%s\"", js_AtomToPrintableString(cx, atom)); } else { jsval v = OBJECT_TO_JSVAL(JSID_TO_OBJECT(sprop->id)); fprintf(fp, " \"%s\"", js_ValueToPrintableString(cx, v)); } #define DUMP_ATTR(name) if (sprop->attrs & JSPROP_##name) fputs(" " #name, fp) DUMP_ATTR(ENUMERATE); DUMP_ATTR(READONLY); DUMP_ATTR(PERMANENT); DUMP_ATTR(EXPORTED); DUMP_ATTR(GETTER); DUMP_ATTR(SETTER); #undef DUMP_ATTR fprintf(fp, " slot %lu flags %x shortid %d\n", (unsigned long)sprop->slot, sprop->flags, sprop->shortid); } } static JSBool DumpStats(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) { uintN i; JSString *str; const char *bytes; JSAtom *atom; JSObject *obj2; JSProperty *prop; jsval value; for (i = 0; i < argc; i++) { str = JS_ValueToString(cx, argv[i]); if (!str) return JS_FALSE; bytes = JS_GetStringBytes(str); if (strcmp(bytes, "arena") == 0) { #ifdef JS_ARENAMETER JS_DumpArenaStats(stdout); #endif } else if (strcmp(bytes, "atom") == 0) { DumpAtomArgs args; fprintf(gOutFile, "\natom table contents:\n"); args.cx = cx; args.fp = stdout; JS_HashTableEnumerateEntries(cx->runtime->atomState.table, DumpAtom, &args); #ifdef HASHMETER JS_HashTableDumpMeter(cx->runtime->atomState.table, DumpAtom, stdout); #endif } else if (strcmp(bytes, "global") == 0) { DumpScope(cx, cx->globalObject, stdout); } else { atom = js_Atomize(cx, bytes, JS_GetStringLength(str), 0); if (!atom) return JS_FALSE; if (!js_FindProperty(cx, ATOM_TO_JSID(atom), &obj, &obj2, &prop)) return JS_FALSE; if (prop) { OBJ_DROP_PROPERTY(cx, obj2, prop); if (!OBJ_GET_PROPERTY(cx, obj, ATOM_TO_JSID(atom), &value)) return JS_FALSE; } if (!prop || !JSVAL_IS_OBJECT(value)) { fprintf(gErrFile, "js: invalid stats argument %s\n", bytes); continue; } obj = JSVAL_TO_OBJECT(value); if (obj) DumpScope(cx, obj, stdout); } } return JS_TRUE; } #endif /* DEBUG */ #ifdef TEST_EXPORT static JSBool DoExport(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) { JSAtom *atom; JSObject *obj2; JSProperty *prop; JSBool ok; uintN attrs; if (argc != 2) { JS_ReportErrorNumber(cx, my_GetErrorMessage, NULL, JSSMSG_DOEXP_USAGE); return JS_FALSE; } if (!JS_ValueToObject(cx, argv[0], &obj)) return JS_FALSE; argv[0] = OBJECT_TO_JSVAL(obj); atom = js_ValueToStringAtom(cx, argv[1]); if (!atom) return JS_FALSE; if (!OBJ_LOOKUP_PROPERTY(cx, obj, ATOM_TO_JSID(atom), &obj2, &prop)) return JS_FALSE; if (!prop) { ok = OBJ_DEFINE_PROPERTY(cx, obj, id, JSVAL_VOID, NULL, NULL, JSPROP_EXPORTED, NULL); } else { ok = OBJ_GET_ATTRIBUTES(cx, obj, ATOM_TO_JSID(atom), prop, &attrs); if (ok) { attrs |= JSPROP_EXPORTED; ok = OBJ_SET_ATTRIBUTES(cx, obj, ATOM_TO_JSID(atom), prop, &attrs); } OBJ_DROP_PROPERTY(cx, obj2, prop); } return ok; } #endif #ifdef TEST_CVTARGS #include static const char * EscapeWideString(jschar *w) { static char enuf[80]; static char hex[] = "0123456789abcdef"; jschar u; unsigned char b, c; int i, j; if (!w) return ""; for (i = j = 0; i < sizeof enuf - 1; i++, j++) { u = w[j]; if (u == 0) break; b = (unsigned char)(u >> 8); c = (unsigned char)(u); if (b) { if (i >= sizeof enuf - 6) break; enuf[i++] = '\\'; enuf[i++] = 'u'; enuf[i++] = hex[b >> 4]; enuf[i++] = hex[b & 15]; enuf[i++] = hex[c >> 4]; enuf[i] = hex[c & 15]; } else if (!isprint(c)) { if (i >= sizeof enuf - 4) break; enuf[i++] = '\\'; enuf[i++] = 'x'; enuf[i++] = hex[c >> 4]; enuf[i] = hex[c & 15]; } else { enuf[i] = (char)c; } } enuf[i] = 0; return enuf; } #include static JSBool ZZ_formatter(JSContext *cx, const char *format, JSBool fromJS, jsval **vpp, va_list *app) { jsval *vp; va_list ap; jsdouble re, im; printf("entering ZZ_formatter"); vp = *vpp; ap = *app; if (fromJS) { if (!JS_ValueToNumber(cx, vp[0], &re)) return JS_FALSE; if (!JS_ValueToNumber(cx, vp[1], &im)) return JS_FALSE; *va_arg(ap, jsdouble *) = re; *va_arg(ap, jsdouble *) = im; } else { re = va_arg(ap, jsdouble); im = va_arg(ap, jsdouble); if (!JS_NewNumberValue(cx, re, &vp[0])) return JS_FALSE; if (!JS_NewNumberValue(cx, im, &vp[1])) return JS_FALSE; } *vpp = vp + 2; *app = ap; printf("leaving ZZ_formatter"); return JS_TRUE; } static JSBool ConvertArgs(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) { JSBool b = JS_FALSE; jschar c = 0; int32 i = 0, j = 0; uint32 u = 0; jsdouble d = 0, I = 0, re = 0, im = 0; char *s = NULL; JSString *str = NULL; jschar *w = NULL; JSObject *obj2 = NULL; JSFunction *fun = NULL; jsval v = JSVAL_VOID; JSBool ok; if (!JS_AddArgumentFormatter(cx, "ZZ", ZZ_formatter)) return JS_FALSE;; ok = JS_ConvertArguments(cx, argc, argv, "b/ciujdIsSWofvZZ*", &b, &c, &i, &u, &j, &d, &I, &s, &str, &w, &obj2, &fun, &v, &re, &im); JS_RemoveArgumentFormatter(cx, "ZZ"); if (!ok) return JS_FALSE; fprintf(gOutFile, "b %u, c %x (%c), i %ld, u %lu, j %ld\n", b, c, (char)c, i, u, j); fprintf(gOutFile, "d %g, I %g, s %s, S %s, W %s, obj %s, fun %s\n" "v %s, re %g, im %g\n", d, I, s, str ? JS_GetStringBytes(str) : "", EscapeWideString(w), JS_GetStringBytes(JS_ValueToString(cx, OBJECT_TO_JSVAL(obj2))), fun ? JS_GetStringBytes(JS_DecompileFunction(cx, fun, 4)) : "", JS_GetStringBytes(JS_ValueToString(cx, v)), re, im); return JS_TRUE; } #endif static JSBool BuildDate(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) { char version[20] = "\n"; #if JS_VERSION < 150 sprintf(version, " for version %d\n", JS_VERSION); #endif fprintf(gOutFile, "built on %s at %s%s", __DATE__, __TIME__, version); return JS_TRUE; } static JSBool Clear(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) { if (argc != 0 && !JS_ValueToObject(cx, argv[0], &obj)) return JS_FALSE; JS_ClearScope(cx, obj); return JS_TRUE; } static JSBool Intern(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) { JSString *str; str = JS_ValueToString(cx, argv[0]); if (!str) return JS_FALSE; if (!JS_InternUCStringN(cx, JS_GetStringChars(str), JS_GetStringLength(str))) { return JS_FALSE; } return JS_TRUE; } static JSBool Clone(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) { JSFunction *fun; JSObject *funobj, *parent, *clone; fun = JS_ValueToFunction(cx, argv[0]); if (!fun) return JS_FALSE; funobj = JS_GetFunctionObject(fun); if (argc > 1) { if (!JS_ValueToObject(cx, argv[1], &parent)) return JS_FALSE; } else { parent = JS_GetParent(cx, funobj); } clone = JS_CloneFunctionObject(cx, funobj, parent); if (!clone) return JS_FALSE; *rval = OBJECT_TO_JSVAL(clone); return JS_TRUE; } static JSBool Seal(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) { JSObject *target; JSBool deep = JS_FALSE; if (!JS_ConvertArguments(cx, argc, argv, "o/b", &target, &deep)) return JS_FALSE; if (!target) return JS_TRUE; return JS_SealObject(cx, target, deep); } static JSBool GetPDA(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) { JSObject *vobj, *aobj, *pdobj; JSBool ok; JSPropertyDescArray pda; JSPropertyDesc *pd; uint32 i; jsval v; if (!JS_ValueToObject(cx, argv[0], &vobj)) return JS_FALSE; if (!vobj) return JS_TRUE; aobj = JS_NewArrayObject(cx, 0, NULL); if (!aobj) return JS_FALSE; *rval = OBJECT_TO_JSVAL(aobj); ok = JS_GetPropertyDescArray(cx, vobj, &pda); if (!ok) return JS_FALSE; pd = pda.array; for (i = 0; i < pda.length; i++) { pdobj = JS_NewObject(cx, NULL, NULL, NULL); if (!pdobj) { ok = JS_FALSE; break; } ok = JS_SetProperty(cx, pdobj, "id", &pd->id) && JS_SetProperty(cx, pdobj, "value", &pd->value) && (v = INT_TO_JSVAL(pd->flags), JS_SetProperty(cx, pdobj, "flags", &v)) && (v = INT_TO_JSVAL(pd->slot), JS_SetProperty(cx, pdobj, "slot", &v)) && JS_SetProperty(cx, pdobj, "alias", &pd->alias); if (!ok) break; v = OBJECT_TO_JSVAL(pdobj); ok = JS_SetElement(cx, aobj, i, &v); if (!ok) break; } JS_PutPropertyDescArray(cx, &pda); return ok; } static JSBool GetSLX(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) { JSScript *script; script = ValueToScript(cx, argv[0]); if (!script) return JS_FALSE; *rval = INT_TO_JSVAL(js_GetScriptLineExtent(script)); return JS_TRUE; } static JSBool ToInt32(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) { int32 i; if (!JS_ValueToInt32(cx, argv[0], &i)) return JS_FALSE; return JS_NewNumberValue(cx, i, rval); } static JSBool StringsAreUtf8(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) { *rval = JS_CStringsAreUTF8() ? JSVAL_TRUE : JSVAL_FALSE; return JS_TRUE; } static const char* badUtf8 = "...\xC0..."; static const char* bigUtf8 = "...\xFB\xBF\xBF\xBF\xBF..."; static const jschar badSurrogate[] = { 'A', 'B', 'C', 0xDEEE, 'D', 'E', 0 }; static JSBool TestUtf8(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) { intN mode = 1; jschar chars[20]; size_t charsLength = 5; char bytes[20]; size_t bytesLength = 20; if (argc && !JS_ValueToInt32(cx, *argv, &mode)) return JS_FALSE; /* The following throw errors if compiled with UTF-8. */ switch (mode) { /* mode 1: malformed UTF-8 string. */ case 1: JS_NewStringCopyZ(cx, badUtf8); break; /* mode 2: big UTF-8 character. */ case 2: JS_NewStringCopyZ(cx, bigUtf8); break; /* mode 3: bad surrogate character. */ case 3: JS_EncodeCharacters(cx, badSurrogate, 6, bytes, &bytesLength); break; /* mode 4: use a too small buffer. */ case 4: JS_DecodeBytes(cx, "1234567890", 10, chars, &charsLength); break; default: JS_ReportError(cx, "invalid mode parameter"); return JS_FALSE; } return !JS_IsExceptionPending (cx); } static JSBool ThrowError(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) { JS_ReportError(cx, "This is an error"); return JS_FALSE; } #define LAZY_STANDARD_CLASSES /* A class for easily testing the inner/outer object callbacks. */ typedef struct ComplexObject { JSBool isInner; JSObject *inner; JSObject *outer; } ComplexObject; static JSObject * split_create_outer(JSContext *cx); static JSObject * split_create_inner(JSContext *cx, JSObject *outer); static ComplexObject * split_get_private(JSContext *cx, JSObject *obj); JS_STATIC_DLL_CALLBACK(JSBool) split_addProperty(JSContext *cx, JSObject *obj, jsval id, jsval *vp) { ComplexObject *cpx; jsid asId; cpx = split_get_private(cx, obj); if (!cpx) return JS_TRUE; if (!cpx->isInner && cpx->inner) { /* Make sure to define this property on the inner object. */ if (!JS_ValueToId(cx, *vp, &asId)) return JS_FALSE; return OBJ_DEFINE_PROPERTY(cx, cpx->inner, asId, *vp, NULL, NULL, JSPROP_ENUMERATE, NULL); } return JS_TRUE; } JS_STATIC_DLL_CALLBACK(JSBool) split_getProperty(JSContext *cx, JSObject *obj, jsval id, jsval *vp) { ComplexObject *cpx; cpx = split_get_private(cx, obj); if (!cpx) return JS_TRUE; if (!cpx->isInner && cpx->inner) { if (JSVAL_IS_STRING(id)) { JSString *str; str = JSVAL_TO_STRING(id); return JS_GetUCProperty(cx, cpx->inner, JS_GetStringChars(str), JS_GetStringLength(str), vp); } if (JSVAL_IS_INT(id)) return JS_GetElement(cx, cpx->inner, JSVAL_TO_INT(id), vp); return JS_TRUE; } return JS_TRUE; } JS_STATIC_DLL_CALLBACK(JSBool) split_setProperty(JSContext *cx, JSObject *obj, jsval id, jsval *vp) { ComplexObject *cpx; cpx = split_get_private(cx, obj); if (!cpx) return JS_TRUE; if (!cpx->isInner && cpx->inner) { if (JSVAL_IS_STRING(id)) { JSString *str; str = JSVAL_TO_STRING(id); return JS_SetUCProperty(cx, cpx->inner, JS_GetStringChars(str), JS_GetStringLength(str), vp); } if (JSVAL_IS_INT(id)) return JS_SetElement(cx, cpx->inner, JSVAL_TO_INT(id), vp); return JS_TRUE; } return JS_TRUE; } JS_STATIC_DLL_CALLBACK(JSBool) split_delProperty(JSContext *cx, JSObject *obj, jsval id, jsval *vp) { ComplexObject *cpx; jsid asId; cpx = split_get_private(cx, obj); if (!cpx) return JS_TRUE; if (!cpx->isInner && cpx->inner) { /* Make sure to define this property on the inner object. */ if (!JS_ValueToId(cx, *vp, &asId)) return JS_FALSE; return OBJ_DELETE_PROPERTY(cx, cpx->inner, asId, vp); } return JS_TRUE; } JS_STATIC_DLL_CALLBACK(JSBool) split_enumerate(JSContext *cx, JSObject *obj, JSIterateOp enum_op, jsval *statep, jsid *idp) { ComplexObject *cpx; JSObject *iterator; switch (enum_op) { case JSENUMERATE_INIT: cpx = JS_GetPrivate(cx, obj); if (!cpx->isInner && cpx->inner) obj = cpx->inner; iterator = JS_NewPropertyIterator(cx, obj); if (!iterator) return JS_FALSE; *statep = OBJECT_TO_JSVAL(iterator); if (idp) *idp = JSVAL_ZERO; break; case JSENUMERATE_NEXT: iterator = (JSObject*)JSVAL_TO_OBJECT(*statep); if (!JS_NextProperty(cx, iterator, idp)) return JS_FALSE; if (*idp != JSVAL_VOID) break; /* Fall through. */ case JSENUMERATE_DESTROY: /* Let GC at our iterator object. */ *statep = JSVAL_NULL; break; } return JS_TRUE; } JS_STATIC_DLL_CALLBACK(JSBool) split_resolve(JSContext *cx, JSObject *obj, jsval id, uintN flags, JSObject **objp) { ComplexObject *cpx; cpx = split_get_private(cx, obj); if (!cpx) return JS_TRUE; if (!cpx->isInner && cpx->inner) { jsid asId; JSProperty *prop; if (!JS_ValueToId(cx, id, &asId)) return JS_FALSE; if (!OBJ_LOOKUP_PROPERTY(cx, cpx->inner, asId, objp, &prop)) return JS_FALSE; if (prop) OBJ_DROP_PROPERTY(cx, cpx->inner, prop); return JS_TRUE; } #ifdef LAZY_STANDARD_CLASSES if (!(flags & JSRESOLVE_ASSIGNING)) { JSBool resolved; if (!JS_ResolveStandardClass(cx, obj, id, &resolved)) return JS_FALSE; if (resolved) { *objp = obj; return JS_TRUE; } } #endif /* XXX For additional realism, let's resolve some random property here. */ return JS_TRUE; } JS_STATIC_DLL_CALLBACK(void) split_finalize(JSContext *cx, JSObject *obj) { JS_free(cx, JS_GetPrivate(cx, obj)); } JS_STATIC_DLL_CALLBACK(uint32) split_mark(JSContext *cx, JSObject *obj, void *arg) { ComplexObject *cpx; cpx = JS_GetPrivate(cx, obj); if (!cpx->isInner && cpx->inner) { /* Mark the inner object. */ JS_MarkGCThing(cx, cpx->inner, "ComplexObject.inner", arg); } return 0; } JS_STATIC_DLL_CALLBACK(JSObject *) split_outerObject(JSContext *cx, JSObject *obj) { ComplexObject *cpx; cpx = JS_GetPrivate(cx, obj); return cpx->isInner ? cpx->outer : obj; } JS_STATIC_DLL_CALLBACK(JSObject *) split_innerObject(JSContext *cx, JSObject *obj) { ComplexObject *cpx; cpx = JS_GetPrivate(cx, obj); return !cpx->isInner ? cpx->inner : obj; } static JSExtendedClass split_global_class = { {"split_global", JSCLASS_NEW_RESOLVE | JSCLASS_HAS_PRIVATE | JSCLASS_IS_EXTENDED, split_addProperty, split_delProperty, split_getProperty, split_setProperty, (JSEnumerateOp)split_enumerate, (JSResolveOp)split_resolve, JS_ConvertStub, split_finalize, NULL, NULL, NULL, NULL, NULL, NULL, split_mark, NULL}, NULL, split_outerObject, split_innerObject, NULL, NULL, NULL, NULL, NULL }; JSObject * split_create_outer(JSContext *cx) { ComplexObject *cpx; JSObject *obj; cpx = JS_malloc(cx, sizeof *obj); if (!cpx) return NULL; cpx->outer = NULL; cpx->inner = NULL; cpx->isInner = JS_FALSE; obj = JS_NewObject(cx, &split_global_class.base, NULL, NULL); if (!obj) { JS_free(cx, cpx); return NULL; } JS_ASSERT(!JS_GetParent(cx, obj)); if (!JS_SetPrivate(cx, obj, cpx)) { JS_free(cx, cpx); return NULL; } return obj; } static JSObject * split_create_inner(JSContext *cx, JSObject *outer) { ComplexObject *cpx, *outercpx; JSObject *obj; JS_ASSERT(JS_GET_CLASS(cx, outer) == &split_global_class.base); cpx = JS_malloc(cx, sizeof *cpx); if (!cpx) return NULL; cpx->outer = outer; cpx->inner = NULL; cpx->isInner = JS_TRUE; obj = JS_NewObject(cx, &split_global_class.base, NULL, NULL); if (!obj || !JS_SetParent(cx, obj, NULL) || !JS_SetPrivate(cx, obj, cpx)) { JS_free(cx, cpx); return NULL; } outercpx = JS_GetPrivate(cx, outer); outercpx->inner = obj; return obj; } static ComplexObject * split_get_private(JSContext *cx, JSObject *obj) { do { if (JS_GET_CLASS(cx, obj) == &split_global_class.base) return JS_GetPrivate(cx, obj); obj = JS_GetParent(cx, obj); } while (obj); return NULL; } static JSBool sandbox_enumerate(JSContext *cx, JSObject *obj) { jsval v; JSBool b; if (!JS_GetProperty(cx, obj, "lazy", &v) || !JS_ValueToBoolean(cx, v, &b)) return JS_FALSE; return !b || JS_EnumerateStandardClasses(cx, obj); } static JSBool sandbox_resolve(JSContext *cx, JSObject *obj, jsval id, uintN flags, JSObject **objp) { jsval v; JSBool b, resolved; if (!JS_GetProperty(cx, obj, "lazy", &v) || !JS_ValueToBoolean(cx, v, &b)) return JS_FALSE; if (b && (flags & JSRESOLVE_ASSIGNING) == 0) { if (!JS_ResolveStandardClass(cx, obj, id, &resolved)) return JS_FALSE; if (resolved) { *objp = obj; return JS_TRUE; } } *objp = NULL; return JS_TRUE; } static JSClass sandbox_class = { "sandbox", JSCLASS_NEW_RESOLVE, JS_PropertyStub, JS_PropertyStub, JS_PropertyStub, JS_PropertyStub, sandbox_enumerate, (JSResolveOp)sandbox_resolve, JS_ConvertStub, JS_FinalizeStub, JSCLASS_NO_OPTIONAL_MEMBERS }; static JSBool EvalInContext(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) { JSString *str; JSObject *sobj; JSContext *scx; const jschar *src; size_t srclen; JSBool lazy, ok; jsval v; JSStackFrame *fp; sobj = NULL; if (!JS_ConvertArguments(cx, argc, argv, "S / o", &str, &sobj)) return JS_FALSE; scx = JS_NewContext(JS_GetRuntime(cx), gStackChunkSize); if (!scx) { JS_ReportOutOfMemory(cx); return JS_FALSE; } src = JS_GetStringChars(str); srclen = JS_GetStringLength(str); lazy = JS_FALSE; if (srclen == 4 && src[0] == 'l' && src[1] == 'a' && src[2] == 'z' && src[3] == 'y') { lazy = JS_TRUE; srclen = 0; } if (!sobj) { sobj = JS_NewObject(scx, &sandbox_class, NULL, NULL); if (!sobj || (!lazy && !JS_InitStandardClasses(scx, sobj))) { ok = JS_FALSE; goto out; } v = BOOLEAN_TO_JSVAL(v); ok = JS_SetProperty(cx, sobj, "lazy", &v); if (!ok) goto out; } if (srclen == 0) { *rval = OBJECT_TO_JSVAL(sobj); ok = JS_TRUE; } else { fp = JS_GetScriptedCaller(cx, NULL); ok = JS_EvaluateUCScript(scx, sobj, src, srclen, fp->script->filename, JS_PCToLineNumber(cx, fp->script, fp->pc), rval); } out: JS_DestroyContext(scx); return ok; } static JSFunctionSpec shell_functions[] = { {"version", Version, 0,0,0}, {"options", Options, 0,0,0}, {"load", Load, 1,0,0}, {"readline", ReadLine, 0,0,0}, {"print", Print, 0,0,0}, {"help", Help, 0,0,0}, {"quit", Quit, 0,0,0}, {"gc", GC, 0,0,0}, {"trap", Trap, 3,0,0}, {"untrap", Untrap, 2,0,0}, {"line2pc", LineToPC, 0,0,0}, {"pc2line", PCToLine, 0,0,0}, {"stringsAreUtf8", StringsAreUtf8, 0,0,0}, {"testUtf8", TestUtf8, 1,0,0}, {"throwError", ThrowError, 0,0,0}, #ifdef DEBUG {"dis", Disassemble, 1,0,0}, {"dissrc", DisassWithSrc, 1,0,0}, {"notes", Notes, 1,0,0}, {"tracing", Tracing, 0,0,0}, {"stats", DumpStats, 1,0,0}, #endif #ifdef TEST_EXPORT {"xport", DoExport, 2,0,0}, #endif #ifdef TEST_CVTARGS {"cvtargs", ConvertArgs, 0,0,12}, #endif {"build", BuildDate, 0,0,0}, {"clear", Clear, 0,0,0}, {"intern", Intern, 1,0,0}, {"clone", Clone, 1,0,0}, {"seal", Seal, 1,0,1}, {"getpda", GetPDA, 1,0,0}, {"getslx", GetSLX, 1,0,0}, {"toint32", ToInt32, 1,0,0}, {"evalcx", EvalInContext, 1,0,0}, {NULL,NULL,0,0,0} }; /* NOTE: These must be kept in sync with the above. */ static char *shell_help_messages[] = { "version([number]) Get or set JavaScript version number", "options([option ...]) Get or toggle JavaScript options", "load(['foo.js' ...]) Load files named by string arguments", "readline() Read a single line from stdin", "print([exp ...]) Evaluate and print expressions", "help([name ...]) Display usage and help messages", "quit() Quit the shell", "gc() Run the garbage collector", "trap([fun, [pc,]] exp) Trap bytecode execution", "untrap(fun[, pc]) Remove a trap", "line2pc([fun,] line) Map line number to PC", "pc2line(fun[, pc]) Map PC to line number", "stringsAreUTF8() Check if strings are UTF-8 encoded", "testUTF8(mode) Perform UTF-8 tests (modes are 1 to 4)", "throwError() Throw an error from JS_ReportError", #ifdef DEBUG "dis([fun]) Disassemble functions into bytecodes", "dissrc([fun]) Disassemble functions with source lines", "notes([fun]) Show source notes for functions", "tracing([toggle]) Turn tracing on or off", "stats([string ...]) Dump 'arena', 'atom', 'global' stats", #endif #ifdef TEST_EXPORT "xport(obj, id) Export identified property from object", #endif #ifdef TEST_CVTARGS "cvtargs(b, c, ...) Test JS_ConvertArguments", #endif "build() Show build date and time", "clear([obj]) Clear properties of object", "intern(str) Internalize str in the atom table", "clone(fun[, scope]) Clone function object", "seal(obj[, deep]) Seal object, or object graph if deep", "getpda(obj) Get the property descriptors for obj", "getslx(obj) Get script line extent", "toint32(n) Testing hook for JS_ValueToInt32", "evalcx(s[, o]) Evaluate s in optional sandbox object o\n" " if (s == '' && !o) return new o with eager standard classes\n" " if (s == 'lazy' && !o) return new o with lazy standard classes", 0 }; static void ShowHelpHeader(void) { fprintf(gOutFile, "%-14s %-22s %s\n", "Command", "Usage", "Description"); fprintf(gOutFile, "%-14s %-22s %s\n", "=======", "=====", "==========="); } static void ShowHelpForCommand(uintN n) { fprintf(gOutFile, "%-14.14s %s\n", shell_functions[n].name, shell_help_messages[n]); } static JSObject * split_setup(JSContext *cx) { JSObject *outer, *inner, *arguments; outer = split_create_outer(cx); if (!outer) return NULL; JS_SetGlobalObject(cx, outer); inner = split_create_inner(cx, outer); if (!inner) return NULL; if (!JS_DefineFunctions(cx, inner, shell_functions)) return NULL; JS_ClearScope(cx, outer); /* Create a dummy arguments object. */ arguments = JS_NewArrayObject(cx, 0, NULL); if (!arguments || !JS_DefineProperty(cx, inner, "arguments", OBJECT_TO_JSVAL(arguments), NULL, NULL, 0)) { return NULL; } #ifndef LAZY_STANDARD_CLASSES if (!JS_InitStandardClasses(cx, inner)) return NULL; #endif return inner; } static JSBool Help(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) { uintN i, j; int did_header, did_something; JSType type; JSFunction *fun; JSString *str; const char *bytes; fprintf(gOutFile, "%s\n", JS_GetImplementationVersion()); if (argc == 0) { ShowHelpHeader(); for (i = 0; shell_functions[i].name; i++) ShowHelpForCommand(i); } else { did_header = 0; for (i = 0; i < argc; i++) { did_something = 0; type = JS_TypeOfValue(cx, argv[i]); if (type == JSTYPE_FUNCTION) { fun = JS_ValueToFunction(cx, argv[i]); str = fun->atom ? ATOM_TO_STRING(fun->atom) : NULL; } else if (type == JSTYPE_STRING) { str = JSVAL_TO_STRING(argv[i]); } else { str = NULL; } if (str) { bytes = JS_GetStringBytes(str); for (j = 0; shell_functions[j].name; j++) { if (!strcmp(bytes, shell_functions[j].name)) { if (!did_header) { did_header = 1; ShowHelpHeader(); } did_something = 1; ShowHelpForCommand(j); break; } } } if (!did_something) { str = JS_ValueToString(cx, argv[i]); if (!str) return JS_FALSE; fprintf(gErrFile, "Sorry, no help for %s\n", JS_GetStringBytes(str)); } } } return JS_TRUE; } /* * Define a JS object called "it". Give it class operations that printf why * they're being called for tutorial purposes. */ enum its_tinyid { ITS_COLOR, ITS_HEIGHT, ITS_WIDTH, ITS_FUNNY, ITS_ARRAY, ITS_RDONLY }; static JSPropertySpec its_props[] = { {"color", ITS_COLOR, JSPROP_ENUMERATE, NULL, NULL}, {"height", ITS_HEIGHT, JSPROP_ENUMERATE, NULL, NULL}, {"width", ITS_WIDTH, JSPROP_ENUMERATE, NULL, NULL}, {"funny", ITS_FUNNY, JSPROP_ENUMERATE, NULL, NULL}, {"array", ITS_ARRAY, JSPROP_ENUMERATE, NULL, NULL}, {"rdonly", ITS_RDONLY, JSPROP_READONLY, NULL, NULL}, {NULL,0,0,NULL,NULL} }; static JSBool its_item(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) { *rval = OBJECT_TO_JSVAL(obj); if (argc != 0) JS_SetCallReturnValue2(cx, argv[0]); return JS_TRUE; } static JSBool its_bindMethod(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) { char *name; JSObject *method; if (!JS_ConvertArguments(cx, argc, argv, "so", &name, &method)) return JS_FALSE; *rval = OBJECT_TO_JSVAL(method); if (JS_TypeOfValue(cx, *rval) != JSTYPE_FUNCTION) { JSString *valstr = JS_ValueToString(cx, *rval); if (valstr) { JS_ReportError(cx, "can't bind method %s to non-callable object %s", name, JS_GetStringBytes(valstr)); } return JS_FALSE; } if (!JS_DefineProperty(cx, obj, name, *rval, NULL, NULL, JSPROP_ENUMERATE)) return JS_FALSE; return JS_SetParent(cx, method, obj); } static JSFunctionSpec its_methods[] = { {"item", its_item, 0,0,0}, {"bindMethod", its_bindMethod, 2,0,0}, {NULL,NULL,0,0,0} }; #ifdef JSD_LOWLEVEL_SOURCE /* * This facilitates sending source to JSD (the debugger system) in the shell * where the source is loaded using the JSFILE hack in jsscan. The function * below is used as a callback for the jsdbgapi JS_SetSourceHandler hook. * A more normal embedding (e.g. mozilla) loads source itself and can send * source directly to JSD without using this hook scheme. */ static void SendSourceToJSDebugger(const char *filename, uintN lineno, jschar *str, size_t length, void **listenerTSData, JSDContext* jsdc) { JSDSourceText *jsdsrc = (JSDSourceText *) *listenerTSData; if (!jsdsrc) { if (!filename) filename = "typein"; if (1 == lineno) { jsdsrc = JSD_NewSourceText(jsdc, filename); } else { jsdsrc = JSD_FindSourceForURL(jsdc, filename); if (jsdsrc && JSD_SOURCE_PARTIAL != JSD_GetSourceStatus(jsdc, jsdsrc)) { jsdsrc = NULL; } } } if (jsdsrc) { jsdsrc = JSD_AppendUCSourceText(jsdc,jsdsrc, str, length, JSD_SOURCE_PARTIAL); } *listenerTSData = jsdsrc; } #endif /* JSD_LOWLEVEL_SOURCE */ static JSBool its_noisy; /* whether to be noisy when finalizing it */ static JSBool its_addProperty(JSContext *cx, JSObject *obj, jsval id, jsval *vp) { if (its_noisy) { fprintf(gOutFile, "adding its property %s,", JS_GetStringBytes(JS_ValueToString(cx, id))); fprintf(gOutFile, " initial value %s\n", JS_GetStringBytes(JS_ValueToString(cx, *vp))); } return JS_TRUE; } static JSBool its_delProperty(JSContext *cx, JSObject *obj, jsval id, jsval *vp) { if (its_noisy) { fprintf(gOutFile, "deleting its property %s,", JS_GetStringBytes(JS_ValueToString(cx, id))); fprintf(gOutFile, " current value %s\n", JS_GetStringBytes(JS_ValueToString(cx, *vp))); } return JS_TRUE; } static JSBool its_getProperty(JSContext *cx, JSObject *obj, jsval id, jsval *vp) { if (its_noisy) { fprintf(gOutFile, "getting its property %s,", JS_GetStringBytes(JS_ValueToString(cx, id))); fprintf(gOutFile, " current value %s\n", JS_GetStringBytes(JS_ValueToString(cx, *vp))); } return JS_TRUE; } static JSBool its_setProperty(JSContext *cx, JSObject *obj, jsval id, jsval *vp) { if (its_noisy) { fprintf(gOutFile, "setting its property %s,", JS_GetStringBytes(JS_ValueToString(cx, id))); fprintf(gOutFile, " new value %s\n", JS_GetStringBytes(JS_ValueToString(cx, *vp))); } if (JSVAL_IS_STRING(id) && !strcmp(JS_GetStringBytes(JSVAL_TO_STRING(id)), "noisy")) { return JS_ValueToBoolean(cx, *vp, &its_noisy); } return JS_TRUE; } static JSBool its_enumerate(JSContext *cx, JSObject *obj) { if (its_noisy) fprintf(gOutFile, "enumerate its properties\n"); return JS_TRUE; } static JSBool its_resolve(JSContext *cx, JSObject *obj, jsval id, uintN flags, JSObject **objp) { if (its_noisy) { fprintf(gOutFile, "resolving its property %s, flags {%s,%s,%s}\n", JS_GetStringBytes(JS_ValueToString(cx, id)), (flags & JSRESOLVE_QUALIFIED) ? "qualified" : "", (flags & JSRESOLVE_ASSIGNING) ? "assigning" : "", (flags & JSRESOLVE_DETECTING) ? "detecting" : ""); } return JS_TRUE; } static JSBool its_convert(JSContext *cx, JSObject *obj, JSType type, jsval *vp) { if (its_noisy) fprintf(gOutFile, "converting it to %s type\n", JS_GetTypeName(cx, type)); return JS_TRUE; } static void its_finalize(JSContext *cx, JSObject *obj) { if (its_noisy) fprintf(gOutFile, "finalizing it\n"); } static JSClass its_class = { "It", JSCLASS_NEW_RESOLVE, its_addProperty, its_delProperty, its_getProperty, its_setProperty, its_enumerate, (JSResolveOp)its_resolve, its_convert, its_finalize, JSCLASS_NO_OPTIONAL_MEMBERS }; JSErrorFormatString jsShell_ErrorFormatString[JSErr_Limit] = { #define MSG_DEF(name, number, count, exception, format) \ { format, count, JSEXN_ERR } , #include "jsshell.msg" #undef MSG_DEF }; static const JSErrorFormatString * my_GetErrorMessage(void *userRef, const char *locale, const uintN errorNumber) { if ((errorNumber > 0) && (errorNumber < JSShellErr_Limit)) return &jsShell_ErrorFormatString[errorNumber]; return NULL; } static void my_ErrorReporter(JSContext *cx, const char *message, JSErrorReport *report) { int i, j, k, n; char *prefix, *tmp; const char *ctmp; if (!report) { fprintf(gErrFile, "%s\n", message); return; } /* Conditionally ignore reported warnings. */ if (JSREPORT_IS_WARNING(report->flags) && !reportWarnings) return; prefix = NULL; if (report->filename) prefix = JS_smprintf("%s:", report->filename); if (report->lineno) { tmp = prefix; prefix = JS_smprintf("%s%u: ", tmp ? tmp : "", report->lineno); JS_free(cx, tmp); } if (JSREPORT_IS_WARNING(report->flags)) { tmp = prefix; prefix = JS_smprintf("%s%swarning: ", tmp ? tmp : "", JSREPORT_IS_STRICT(report->flags) ? "strict " : ""); JS_free(cx, tmp); } /* embedded newlines -- argh! */ while ((ctmp = strchr(message, '\n')) != 0) { ctmp++; if (prefix) fputs(prefix, gErrFile); fwrite(message, 1, ctmp - message, gErrFile); message = ctmp; } /* If there were no filename or lineno, the prefix might be empty */ if (prefix) fputs(prefix, gErrFile); fputs(message, gErrFile); if (!report->linebuf) { fputc('\n', gErrFile); goto out; } /* report->linebuf usually ends with a newline. */ n = strlen(report->linebuf); fprintf(gErrFile, ":\n%s%s%s%s", prefix, report->linebuf, (n > 0 && report->linebuf[n-1] == '\n') ? "" : "\n", prefix); n = PTRDIFF(report->tokenptr, report->linebuf, char); for (i = j = 0; i < n; i++) { if (report->linebuf[i] == '\t') { for (k = (j + 8) & ~7; j < k; j++) { fputc('.', gErrFile); } continue; } fputc('.', gErrFile); j++; } fputs("^\n", gErrFile); out: if (!JSREPORT_IS_WARNING(report->flags)) { if (report->errorNumber == JSMSG_OUT_OF_MEMORY) { gExitCode = EXITCODE_OUT_OF_MEMORY; } else { gExitCode = EXITCODE_RUNTIME_ERROR; } } JS_free(cx, prefix); } #if defined(SHELL_HACK) && defined(DEBUG) && defined(XP_UNIX) static JSBool Exec(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) { JSFunction *fun; const char *name, **nargv; uintN i, nargc; JSString *str; pid_t pid; int status; fun = JS_ValueToFunction(cx, argv[-2]); if (!fun) return JS_FALSE; if (!fun->atom) return JS_TRUE; name = JS_GetStringBytes(ATOM_TO_STRING(fun->atom)); nargc = 1 + argc; nargv = JS_malloc(cx, (nargc + 1) * sizeof(char *)); if (!nargv) return JS_FALSE; nargv[0] = name; for (i = 1; i < nargc; i++) { str = JS_ValueToString(cx, argv[i-1]); if (!str) { JS_free(cx, nargv); return JS_FALSE; } nargv[i] = JS_GetStringBytes(str); } nargv[nargc] = 0; pid = fork(); switch (pid) { case -1: perror("js"); break; case 0: (void) execvp(name, (char **)nargv); perror("js"); exit(127); default: while (waitpid(pid, &status, 0) < 0 && errno == EINTR) continue; break; } JS_free(cx, nargv); return JS_TRUE; } #endif static JSBool global_enumerate(JSContext *cx, JSObject *obj) { #ifdef LAZY_STANDARD_CLASSES return JS_EnumerateStandardClasses(cx, obj); #else return JS_TRUE; #endif } static JSBool global_resolve(JSContext *cx, JSObject *obj, jsval id, uintN flags, JSObject **objp) { #ifdef LAZY_STANDARD_CLASSES JSBool resolved; if (!JS_ResolveStandardClass(cx, obj, id, &resolved)) return JS_FALSE; if (resolved) { *objp = obj; return JS_TRUE; } #endif #if defined(SHELL_HACK) && defined(DEBUG) && defined(XP_UNIX) if ((flags & (JSRESOLVE_QUALIFIED | JSRESOLVE_ASSIGNING)) == 0) { /* * Do this expensive hack only for unoptimized Unix builds, which are * not used for benchmarking. */ char *path, *comp, *full; const char *name; JSBool ok, found; JSFunction *fun; if (!JSVAL_IS_STRING(id)) return JS_TRUE; path = getenv("PATH"); if (!path) return JS_TRUE; path = JS_strdup(cx, path); if (!path) return JS_FALSE; name = JS_GetStringBytes(JSVAL_TO_STRING(id)); ok = JS_TRUE; for (comp = strtok(path, ":"); comp; comp = strtok(NULL, ":")) { if (*comp != '\0') { full = JS_smprintf("%s/%s", comp, name); if (!full) { JS_ReportOutOfMemory(cx); ok = JS_FALSE; break; } } else { full = (char *)name; } found = (access(full, X_OK) == 0); if (*comp != '\0') free(full); if (found) { fun = JS_DefineFunction(cx, obj, name, Exec, 0, JSPROP_ENUMERATE); ok = (fun != NULL); if (ok) *objp = obj; break; } } JS_free(cx, path); return ok; } #else return JS_TRUE; #endif } JSClass global_class = { "global", JSCLASS_NEW_RESOLVE | JSCLASS_GLOBAL_FLAGS, JS_PropertyStub, JS_PropertyStub, JS_PropertyStub, JS_PropertyStub, global_enumerate, (JSResolveOp) global_resolve, JS_ConvertStub, JS_FinalizeStub, JSCLASS_NO_OPTIONAL_MEMBERS }; static JSBool env_setProperty(JSContext *cx, JSObject *obj, jsval id, jsval *vp) { /* XXX porting may be easy, but these don't seem to supply setenv by default */ #if !defined XP_BEOS && !defined XP_OS2 && !defined SOLARIS JSString *idstr, *valstr; const char *name, *value; int rv; idstr = JS_ValueToString(cx, id); valstr = JS_ValueToString(cx, *vp); if (!idstr || !valstr) return JS_FALSE; name = JS_GetStringBytes(idstr); value = JS_GetStringBytes(valstr); #if defined XP_WIN || defined HPUX || defined OSF1 || defined IRIX { char *waste = JS_smprintf("%s=%s", name, value); if (!waste) { JS_ReportOutOfMemory(cx); return JS_FALSE; } rv = putenv(waste); #ifdef XP_WIN /* * HPUX9 at least still has the bad old non-copying putenv. * * Per mail from , OSF1 also has a putenv * that will crash if you pass it an auto char array (so it must place * its argument directly in the char *environ[] array). */ free(waste); #endif } #else rv = setenv(name, value, 1); #endif if (rv < 0) { JS_ReportError(cx, "can't set envariable %s to %s", name, value); return JS_FALSE; } *vp = STRING_TO_JSVAL(valstr); #endif /* !defined XP_BEOS && !defined XP_OS2 && !defined SOLARIS */ return JS_TRUE; } static JSBool env_enumerate(JSContext *cx, JSObject *obj) { static JSBool reflected; char **evp, *name, *value; JSString *valstr; JSBool ok; if (reflected) return JS_TRUE; for (evp = (char **)JS_GetPrivate(cx, obj); (name = *evp) != NULL; evp++) { value = strchr(name, '='); if (!value) continue; *value++ = '\0'; valstr = JS_NewStringCopyZ(cx, value); if (!valstr) { ok = JS_FALSE; } else { ok = JS_DefineProperty(cx, obj, name, STRING_TO_JSVAL(valstr), NULL, NULL, JSPROP_ENUMERATE); } value[-1] = '='; if (!ok) return JS_FALSE; } reflected = JS_TRUE; return JS_TRUE; } static JSBool env_resolve(JSContext *cx, JSObject *obj, jsval id, uintN flags, JSObject **objp) { JSString *idstr, *valstr; const char *name, *value; if (flags & JSRESOLVE_ASSIGNING) return JS_TRUE; idstr = JS_ValueToString(cx, id); if (!idstr) return JS_FALSE; name = JS_GetStringBytes(idstr); value = getenv(name); if (value) { valstr = JS_NewStringCopyZ(cx, value); if (!valstr) return JS_FALSE; if (!JS_DefineProperty(cx, obj, name, STRING_TO_JSVAL(valstr), NULL, NULL, JSPROP_ENUMERATE)) { return JS_FALSE; } *objp = obj; } return JS_TRUE; } static JSClass env_class = { "environment", JSCLASS_HAS_PRIVATE | JSCLASS_NEW_RESOLVE, JS_PropertyStub, JS_PropertyStub, JS_PropertyStub, env_setProperty, env_enumerate, (JSResolveOp) env_resolve, JS_ConvertStub, JS_FinalizeStub, JSCLASS_NO_OPTIONAL_MEMBERS }; #ifdef NARCISSUS static JSBool defineProperty(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) { JSString *str; jsval value; JSBool dontDelete, readOnly, dontEnum; const jschar *chars; size_t length; uintN attrs; dontDelete = readOnly = dontEnum = JS_FALSE; if (!JS_ConvertArguments(cx, argc, argv, "Sv/bbb", &str, &value, &dontDelete, &readOnly, &dontEnum)) { return JS_FALSE; } chars = JS_GetStringChars(str); length = JS_GetStringLength(str); attrs = dontEnum ? 0 : JSPROP_ENUMERATE; if (dontDelete) attrs |= JSPROP_PERMANENT; if (readOnly) attrs |= JSPROP_READONLY; return JS_DefineUCProperty(cx, obj, chars, length, value, NULL, NULL, attrs); } static JSBool Evaluate(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) { /* function evaluate(source, filename, lineno) { ... } */ JSString *source; const char *filename = ""; jsuint lineno = 0; uint32 oldopts; JSBool ok; if (argc == 0) { *rval = JSVAL_VOID; return JS_TRUE; } if (!JS_ConvertArguments(cx, argc, argv, "S/su", &source, &filename, &lineno)) { return JS_FALSE; } oldopts = JS_GetOptions(cx); JS_SetOptions(cx, oldopts | JSOPTION_COMPILE_N_GO); ok = JS_EvaluateUCScript(cx, obj, JS_GetStringChars(source), JS_GetStringLength(source), filename, lineno, rval); JS_SetOptions(cx, oldopts); return ok; } #include #include /* * Returns a JS_malloc'd string (that the caller needs to JS_free) * containing the directory (non-leaf) part of |from| prepended to |leaf|. * If |from| is empty or a leaf, MakeAbsolutePathname returns a copy of leaf. * Returns NULL to indicate an error. */ static char * MakeAbsolutePathname(JSContext *cx, const char *from, const char *leaf) { size_t dirlen; char *dir; const char *slash = NULL, *cp; cp = from; while (*cp) { if (*cp == '/' #ifdef XP_WIN || *cp == '\\' #endif ) { slash = cp; } ++cp; } if (!slash) { /* We were given a leaf or |from| was empty. */ return JS_strdup(cx, leaf); } /* Else, we were given a real pathname, return that + the leaf. */ dirlen = slash - from + 1; dir = JS_malloc(cx, dirlen + strlen(leaf) + 1); if (!dir) return NULL; strncpy(dir, from, dirlen); strcpy(dir + dirlen, leaf); /* Note: we can't use strcat here. */ return dir; } static JSBool snarf(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) { JSString *str; const char *filename; char *pathname; JSStackFrame *fp; JSBool ok; off_t cc, len; char *buf; FILE *file; str = JS_ValueToString(cx, argv[0]); if (!str) return JS_FALSE; filename = JS_GetStringBytes(str); /* Get the currently executing script's name. */ fp = JS_GetScriptedCaller(cx, NULL); JS_ASSERT(fp && fp->script->filename); pathname = MakeAbsolutePathname(cx, fp->script->filename, filename); if (!pathname) return JS_FALSE; ok = JS_FALSE; len = 0; buf = NULL; file = fopen(pathname, "rb"); if (!file) { JS_ReportError(cx, "can't open %s: %s", pathname, strerror(errno)); } else { if (fseek(file, 0, SEEK_END) == EOF) { JS_ReportError(cx, "can't seek end of %s", pathname); } else { len = ftell(file); if (fseek(file, 0, SEEK_SET) == EOF) { JS_ReportError(cx, "can't seek start of %s", pathname); } else { buf = JS_malloc(cx, len + 1); if (buf) { cc = fread(buf, 1, len, file); if (cc != len) { JS_free(cx, buf); JS_ReportError(cx, "can't read %s: %s", pathname, (cc < 0) ? strerror(errno) : "short read"); } else { len = (size_t)cc; ok = JS_TRUE; } } } } fclose(file); } JS_free(cx, pathname); if (!ok) { JS_free(cx, buf); return ok; } buf[len] = '\0'; str = JS_NewString(cx, buf, len); if (!str) { JS_free(cx, buf); return JS_FALSE; } *rval = STRING_TO_JSVAL(str); return JS_TRUE; } #endif /* NARCISSUS */ int main(int argc, char **argv, char **envp) { int stackDummy; JSRuntime *rt; JSContext *cx; JSObject *glob, *it, *envobj; int result; #ifdef LIVECONNECT JavaVM *java_vm = NULL; #endif #ifdef JSDEBUGGER_JAVA_UI JNIEnv *java_env; #endif gStackBase = (jsuword)&stackDummy; setlocale(LC_ALL, ""); #ifdef XP_OS2 /* these streams are normally line buffered on OS/2 and need a \n, * * so we need to unbuffer then to get a reasonable prompt */ setbuf(stdout,0); setbuf(stderr,0); #endif gErrFile = stderr; gOutFile = stdout; argc--; argv++; rt = JS_NewRuntime(64L * 1024L * 1024L); if (!rt) return 1; cx = JS_NewContext(rt, gStackChunkSize); if (!cx) return 1; JS_SetErrorReporter(cx, my_ErrorReporter); #ifdef JS_THREADSAFE JS_BeginRequest(cx); #endif glob = JS_NewObject(cx, &global_class, NULL, NULL); if (!glob) return 1; #ifdef LAZY_STANDARD_CLASSES JS_SetGlobalObject(cx, glob); #else if (!JS_InitStandardClasses(cx, glob)) return 1; #endif if (!JS_DefineFunctions(cx, glob, shell_functions)) return 1; it = JS_DefineObject(cx, glob, "it", &its_class, NULL, 0); if (!it) return 1; if (!JS_DefineProperties(cx, it, its_props)) return 1; if (!JS_DefineFunctions(cx, it, its_methods)) return 1; #ifdef PERLCONNECT if (!JS_InitPerlClass(cx, glob)) return 1; #endif #ifdef JSDEBUGGER /* * XXX A command line option to enable debugging (or not) would be good */ _jsdc = JSD_DebuggerOnForUser(rt, NULL, NULL); if (!_jsdc) return 1; JSD_JSContextInUse(_jsdc, cx); #ifdef JSD_LOWLEVEL_SOURCE JS_SetSourceHandler(rt, SendSourceToJSDebugger, _jsdc); #endif /* JSD_LOWLEVEL_SOURCE */ #ifdef JSDEBUGGER_JAVA_UI _jsdjc = JSDJ_CreateContext(); if (! _jsdjc) return 1; JSDJ_SetJSDContext(_jsdjc, _jsdc); java_env = JSDJ_CreateJavaVMAndStartDebugger(_jsdjc); #ifdef LIVECONNECT if (java_env) (*java_env)->GetJavaVM(java_env, &java_vm); #endif /* * XXX This would be the place to wait for the debugger to start. * Waiting would be nice in general, but especially when a js file * is passed on the cmd line. */ #endif /* JSDEBUGGER_JAVA_UI */ #ifdef JSDEBUGGER_C_UI JSDB_InitDebugger(rt, _jsdc, 0); #endif /* JSDEBUGGER_C_UI */ #endif /* JSDEBUGGER */ #ifdef LIVECONNECT if (!JSJ_SimpleInit(cx, glob, java_vm, getenv("CLASSPATH"))) return 1; #endif envobj = JS_DefineObject(cx, glob, "environment", &env_class, NULL, 0); if (!envobj || !JS_SetPrivate(cx, envobj, envp)) return 1; #ifdef NARCISSUS { jsval v; static const char Object_prototype[] = "Object.prototype"; if (!JS_DefineFunction(cx, glob, "snarf", snarf, 1, 0)) return 1; if (!JS_DefineFunction(cx, glob, "evaluate", Evaluate, 3, 0)) return 1; if (!JS_EvaluateScript(cx, glob, Object_prototype, sizeof Object_prototype - 1, NULL, 0, &v)) { return 1; } if (!JS_DefineFunction(cx, JSVAL_TO_OBJECT(v), "__defineProperty__", defineProperty, 5, 0)) { return 1; } } #endif result = ProcessArgs(cx, glob, argv, argc); #ifdef JSDEBUGGER if (_jsdc) JSD_DebuggerOff(_jsdc); #endif /* JSDEBUGGER */ #ifdef JS_THREADSAFE JS_EndRequest(cx); #endif JS_DestroyContext(cx); JS_DestroyRuntime(rt); JS_ShutDown(); return result; } pacparser-1.4.5/src/spidermonkey/js/src/js.mak000066400000000000000000002561701464010763600213340ustar00rootroot00000000000000# Microsoft Developer Studio Generated NMAKE File, Format Version 4.20 # ** DO NOT EDIT ** # TARGTYPE "Win32 (x86) Console Application" 0x0103 # TARGTYPE "Win32 (x86) Dynamic-Link Library" 0x0102 # TARGTYPE "Win32 (x86) Static Library" 0x0104 !IF "$(CFG)" == "" CFG=jsshell - Win32 Debug !MESSAGE No configuration specified. Defaulting to jsshell - Win32 Debug. !ENDIF !IF "$(CFG)" != "js - Win32 Release" && "$(CFG)" != "js - Win32 Debug" &&\ "$(CFG)" != "jsshell - Win32 Release" && "$(CFG)" != "jsshell - Win32 Debug" &&\ "$(CFG)" != "jskwgen - Win32 Release" && "$(CFG)" != "jskwgen - Win32 Debug" &&\ "$(CFG)" != "fdlibm - Win32 Release" && "$(CFG)" != "fdlibm - Win32 Debug" !MESSAGE Invalid configuration "$(CFG)" specified. !MESSAGE You can specify a configuration when running NMAKE on this makefile !MESSAGE by defining the macro CFG on the command line. For example: !MESSAGE !MESSAGE NMAKE /f "js.mak" CFG="jsshell - Win32 Debug" !MESSAGE !MESSAGE Possible choices for configuration are: !MESSAGE !MESSAGE "js - Win32 Release" (based on "Win32 (x86) Dynamic-Link Library") !MESSAGE "js - Win32 Debug" (based on "Win32 (x86) Dynamic-Link Library") !MESSAGE "jsshell - Win32 Release" (based on "Win32 (x86) Console Application") !MESSAGE "jsshell - Win32 Debug" (based on "Win32 (x86) Console Application") !MESSAGE "jskwgen - Win32 Release" (based on "Win32 (x86) Static Library") !MESSAGE "jskwgen - Win32 Debug" (based on "Win32 (x86) Static Library") !MESSAGE "fdlibm - Win32 Release" (based on "Win32 (x86) Static Library") !MESSAGE "fdlibm - Win32 Debug" (based on "Win32 (x86) Static Library") !MESSAGE !ERROR An invalid configuration is specified. !ENDIF !IF "$(OS)" == "Windows_NT" NULL= !ELSE NULL=nul !ENDIF ################################################################################ # Begin Project # PROP Target_Last_Scanned "jsshell - Win32 Debug" !IF "$(CFG)" == "js - Win32 Release" # PROP BASE Use_MFC 0 # PROP BASE Use_Debug_Libraries 0 # PROP BASE Output_Dir "js___Wi1" # PROP BASE Intermediate_Dir "js___Wi1" # PROP BASE Target_Dir "" # PROP Use_MFC 0 # PROP Use_Debug_Libraries 0 # PROP Output_Dir "Release" # PROP Intermediate_Dir "Release" # PROP Target_Dir "" OUTDIR=.\Release INTDIR=.\Release ALL : "fdlibm - Win32 Release" "jskwgen - Win32 Release" "$(OUTDIR)\js32.dll" CLEAN : -@erase "$(INTDIR)\jsapi.obj" -@erase "$(INTDIR)\jsarena.obj" -@erase "$(INTDIR)\jsarray.obj" -@erase "$(INTDIR)\jsatom.obj" -@erase "$(INTDIR)\jsbool.obj" -@erase "$(INTDIR)\jscntxt.obj" -@erase "$(INTDIR)\jsdate.obj" -@erase "$(INTDIR)\jsdbgapi.obj" -@erase "$(INTDIR)\jsdhash.obj" -@erase "$(INTDIR)\jsdtoa.obj" -@erase "$(INTDIR)\jsemit.obj" -@erase "$(INTDIR)\jsexn.obj" -@erase "$(INTDIR)\jsfun.obj" -@erase "$(INTDIR)\jsgc.obj" -@erase "$(INTDIR)\jshash.obj" -@erase "$(INTDIR)\jsinterp.obj" -@erase "$(INTDIR)\jslock.obj" -@erase "$(INTDIR)\jslog2.obj" -@erase "$(INTDIR)\jslong.obj" -@erase "$(INTDIR)\jsmath.obj" -@erase "$(INTDIR)\jsnum.obj" -@erase "$(INTDIR)\jsobj.obj" -@erase "$(INTDIR)\jsopcode.obj" -@erase "$(INTDIR)\jsparse.obj" -@erase "$(INTDIR)\jsprf.obj" -@erase "$(INTDIR)\jsregexp.obj" -@erase "$(INTDIR)\jsscan.obj" -@erase "$(INTDIR)\jsscope.obj" -@erase "$(INTDIR)\jsscript.obj" -@erase "$(INTDIR)\jsstr.obj" -@erase "$(INTDIR)\jsutil.obj" -@erase "$(INTDIR)\jsxdrapi.obj" -@erase "$(INTDIR)\jsxml.obj" -@erase "$(INTDIR)\prmjtime.obj" -@erase "$(INTDIR)\js.pch" -@erase "$(INTDIR)\jsautokw.h" -@erase "$(OUTDIR)\js32.dll" -@erase "$(OUTDIR)\js32.exp" -@erase "$(OUTDIR)\js32.lib" -@$(MAKE) /nologo /$(MAKEFLAGS) /F ".\js.mak" CFG="fdlibm - Win32 Release" clean -@$(MAKE) /nologo /$(MAKEFLAGS) /F ".\js.mak" CFG="jskwgen - Win32 Release" clean "$(OUTDIR)" : if not exist "$(OUTDIR)/$(NULL)" mkdir "$(OUTDIR)" CPP=cl.exe # ADD BASE CPP /nologo /MT /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D _X86_=1 /D "_WINDOWS" /YX /c # ADD CPP /nologo /MD /W3 /GX /O2 /D "NDEBUG" /D _X86_=1 /D "_WINDOWS" /D "WIN32" /D "XP_WIN" /D "JSFILE" /D "EXPORT_JS_API" /I"$(INTDIR)" /YX /c CPP_PROJ=/nologo /MD /W3 /GX /O2 /D "NDEBUG" /D _X86_=1 /D "_WINDOWS" /D "WIN32" /D\ "XP_WIN" /D "JSFILE" /D "EXPORT_JS_API" /Fp"$(INTDIR)/js.pch" /I"$(INTDIR)" /YX\ /Fo"$(INTDIR)/" /c CPP_OBJS=.\Release/ CPP_SBRS=.\. .c{$(CPP_OBJS)}.obj: $(CPP) $(CPP_PROJ) $< .cpp{$(CPP_OBJS)}.obj: $(CPP) $(CPP_PROJ) $< .cxx{$(CPP_OBJS)}.obj: $(CPP) $(CPP_PROJ) $< .c{$(CPP_SBRS)}.sbr: $(CPP) $(CPP_PROJ) $< .cpp{$(CPP_SBRS)}.sbr: $(CPP) $(CPP_PROJ) $< .cxx{$(CPP_SBRS)}.sbr: $(CPP) $(CPP_PROJ) $< MTL=mktyplib.exe # ADD BASE MTL /nologo /D "NDEBUG" /win32 # ADD MTL /nologo /D "NDEBUG" /win32 MTL_PROJ=/nologo /D "NDEBUG" /win32 RSC=rc.exe # ADD BASE RSC /l 0x409 /d "NDEBUG" # ADD RSC /l 0x409 /d "NDEBUG" BSC32=bscmake.exe # ADD BASE BSC32 /nologo # ADD BSC32 /nologo BSC32_FLAGS=/nologo /o"$(OUTDIR)/js.bsc" BSC32_SBRS= \ LINK32=link.exe # ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:windows /dll /machine:I386 # ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:windows /dll /machine:I386 /out:"Release/js32.dll" # SUBTRACT LINK32 /nodefaultlib LINK32_FLAGS=kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib\ advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib\ odbccp32.lib /nologo /subsystem:windows /dll /incremental:no\ /pdb:"$(OUTDIR)/js32.pdb" /machine:I386 /out:"$(OUTDIR)/js32.dll"\ /implib:"$(OUTDIR)/js32.lib" /opt:ref /opt:noicf LINK32_OBJS= \ "$(INTDIR)\jsapi.obj" \ "$(INTDIR)\jsarena.obj" \ "$(INTDIR)\jsarray.obj" \ "$(INTDIR)\jsatom.obj" \ "$(INTDIR)\jsbool.obj" \ "$(INTDIR)\jscntxt.obj" \ "$(INTDIR)\jsdate.obj" \ "$(INTDIR)\jsdbgapi.obj" \ "$(INTDIR)\jsdhash.obj" \ "$(INTDIR)\jsdtoa.obj" \ "$(INTDIR)\jsemit.obj" \ "$(INTDIR)\jsexn.obj" \ "$(INTDIR)\jsfun.obj" \ "$(INTDIR)\jsgc.obj" \ "$(INTDIR)\jshash.obj" \ "$(INTDIR)\jsinterp.obj" \ "$(INTDIR)\jslock.obj" \ "$(INTDIR)\jslog2.obj" \ "$(INTDIR)\jslong.obj" \ "$(INTDIR)\jsmath.obj" \ "$(INTDIR)\jsnum.obj" \ "$(INTDIR)\jsobj.obj" \ "$(INTDIR)\jsopcode.obj" \ "$(INTDIR)\jsparse.obj" \ "$(INTDIR)\jsprf.obj" \ "$(INTDIR)\jsregexp.obj" \ "$(INTDIR)\jsscan.obj" \ "$(INTDIR)\jsscope.obj" \ "$(INTDIR)\jsscript.obj" \ "$(INTDIR)\jsstr.obj" \ "$(INTDIR)\jsutil.obj" \ "$(INTDIR)\jsxdrapi.obj" \ "$(INTDIR)\jsxml.obj" \ "$(INTDIR)\prmjtime.obj" \ "$(OUTDIR)\fdlibm.lib" "$(OUTDIR)\js32.dll" : "$(OUTDIR)" $(DEF_FILE) $(LINK32_OBJS) $(LINK32) @<< $(LINK32_FLAGS) $(LINK32_OBJS) << !ELSEIF "$(CFG)" == "js - Win32 Debug" # PROP BASE Use_MFC 0 # PROP BASE Use_Debug_Libraries 1 # PROP BASE Output_Dir "js___Wi2" # PROP BASE Intermediate_Dir "js___Wi2" # PROP BASE Target_Dir "" # PROP Use_MFC 0 # PROP Use_Debug_Libraries 1 # PROP Output_Dir "Debug" # PROP Intermediate_Dir "Debug" # PROP Target_Dir "" OUTDIR=.\Debug INTDIR=.\Debug ALL : "fdlibm - Win32 Debug" "jskwgen - Win32 Debug" "$(OUTDIR)\js32.dll" CLEAN : -@erase "$(INTDIR)\jsapi.obj" -@erase "$(INTDIR)\jsarena.obj" -@erase "$(INTDIR)\jsarray.obj" -@erase "$(INTDIR)\jsatom.obj" -@erase "$(INTDIR)\jsbool.obj" -@erase "$(INTDIR)\jscntxt.obj" -@erase "$(INTDIR)\jsdate.obj" -@erase "$(INTDIR)\jsdbgapi.obj" -@erase "$(INTDIR)\jsdhash.obj" -@erase "$(INTDIR)\jsdtoa.obj" -@erase "$(INTDIR)\jsemit.obj" -@erase "$(INTDIR)\jsexn.obj" -@erase "$(INTDIR)\jsfun.obj" -@erase "$(INTDIR)\jsgc.obj" -@erase "$(INTDIR)\jshash.obj" -@erase "$(INTDIR)\jsinterp.obj" -@erase "$(INTDIR)\jslock.obj" -@erase "$(INTDIR)\jslog2.obj" -@erase "$(INTDIR)\jslong.obj" -@erase "$(INTDIR)\jsmath.obj" -@erase "$(INTDIR)\jsnum.obj" -@erase "$(INTDIR)\jsobj.obj" -@erase "$(INTDIR)\jsopcode.obj" -@erase "$(INTDIR)\jsparse.obj" -@erase "$(INTDIR)\jsprf.obj" -@erase "$(INTDIR)\jsregexp.obj" -@erase "$(INTDIR)\jsscan.obj" -@erase "$(INTDIR)\jsscope.obj" -@erase "$(INTDIR)\jsscript.obj" -@erase "$(INTDIR)\jsstr.obj" -@erase "$(INTDIR)\jsutil.obj" -@erase "$(INTDIR)\jsxdrapi.obj" -@erase "$(INTDIR)\jsxml.obj" -@erase "$(INTDIR)\prmjtime.obj" -@erase "$(INTDIR)\js.pch" -@erase "$(INTDIR)\jsautokw.h" -@erase "$(OUTDIR)\js32.dll" -@erase "$(OUTDIR)\js32.exp" -@erase "$(OUTDIR)\js32.ilk" -@erase "$(OUTDIR)\js32.lib" -@erase "$(OUTDIR)\js32.pdb" -@$(MAKE) /nologo /$(MAKEFLAGS) /F ".\js.mak" CFG="fdlibm - Win32 Debug" clean -@$(MAKE) /nologo /$(MAKEFLAGS) /F ".\js.mak" CFG="jskwgen - Win32 Debug" clean "$(OUTDIR)" : if not exist "$(OUTDIR)/$(NULL)" mkdir "$(OUTDIR)" CPP=cl.exe # ADD BASE CPP /nologo /MTd /W3 /Gm /GX /Zi /Od /D "WIN32" /D "_DEBUG" /D _X86_=1 /D "_WINDOWS" /YX /c # ADD CPP /nologo /MDd /W3 /Gm /GX /Zi /Od /D "_DEBUG" /D "DEBUG" /D _X86_=1 /D "_WINDOWS" /D "WIN32" /D "XP_WIN" /D "JSFILE" /D "EXPORT_JS_API" /I"$(INTDIR)" /YX /c CPP_PROJ=/nologo /MDd /W3 /Gm /GX /Zi /Od /D "_DEBUG" /D "DEBUG" /D _X86_=1 /D "_WINDOWS"\ /D "WIN32" /D "XP_WIN" /D "JSFILE" /D "EXPORT_JS_API" /Fp"$(INTDIR)/js.pch" /I"$(INTDIR)" /YX\ /Fo"$(INTDIR)/" /Fd"$(INTDIR)/" /c CPP_OBJS=.\Debug/ CPP_SBRS=.\. .c{$(CPP_OBJS)}.obj: $(CPP) $(CPP_PROJ) $< .cpp{$(CPP_OBJS)}.obj: $(CPP) $(CPP_PROJ) $< .cxx{$(CPP_OBJS)}.obj: $(CPP) $(CPP_PROJ) $< .c{$(CPP_SBRS)}.sbr: $(CPP) $(CPP_PROJ) $< .cpp{$(CPP_SBRS)}.sbr: $(CPP) $(CPP_PROJ) $< .cxx{$(CPP_SBRS)}.sbr: $(CPP) $(CPP_PROJ) $< MTL=mktyplib.exe # ADD BASE MTL /nologo /D "_DEBUG" /win32 # ADD MTL /nologo /D "_DEBUG" /win32 MTL_PROJ=/nologo /D "_DEBUG" /win32 RSC=rc.exe # ADD BASE RSC /l 0x409 /d "_DEBUG" # ADD RSC /l 0x409 /d "_DEBUG" BSC32=bscmake.exe # ADD BASE BSC32 /nologo # ADD BSC32 /nologo BSC32_FLAGS=/nologo /o"$(OUTDIR)/js.bsc" BSC32_SBRS= \ LINK32=link.exe # ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:windows /dll /debug /machine:I386 # ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:windows /dll /debug /machine:I386 /out:"Debug/js32.dll" # SUBTRACT LINK32 /nodefaultlib LINK32_FLAGS=kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib\ advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib\ odbccp32.lib /nologo /subsystem:windows /dll /incremental:yes\ /pdb:"$(OUTDIR)/js32.pdb" /debug /machine:I386 /out:"$(OUTDIR)/js32.dll"\ /implib:"$(OUTDIR)/js32.lib" LINK32_OBJS= \ "$(INTDIR)\jsapi.obj" \ "$(INTDIR)\jsarena.obj" \ "$(INTDIR)\jsarray.obj" \ "$(INTDIR)\jsatom.obj" \ "$(INTDIR)\jsbool.obj" \ "$(INTDIR)\jscntxt.obj" \ "$(INTDIR)\jsdate.obj" \ "$(INTDIR)\jsdbgapi.obj" \ "$(INTDIR)\jsdhash.obj" \ "$(INTDIR)\jsdtoa.obj" \ "$(INTDIR)\jsemit.obj" \ "$(INTDIR)\jsexn.obj" \ "$(INTDIR)\jsfun.obj" \ "$(INTDIR)\jsgc.obj" \ "$(INTDIR)\jshash.obj" \ "$(INTDIR)\jsinterp.obj" \ "$(INTDIR)\jslock.obj" \ "$(INTDIR)\jslog2.obj" \ "$(INTDIR)\jslong.obj" \ "$(INTDIR)\jsmath.obj" \ "$(INTDIR)\jsnum.obj" \ "$(INTDIR)\jsobj.obj" \ "$(INTDIR)\jsopcode.obj" \ "$(INTDIR)\jsparse.obj" \ "$(INTDIR)\jsprf.obj" \ "$(INTDIR)\jsregexp.obj" \ "$(INTDIR)\jsscan.obj" \ "$(INTDIR)\jsscope.obj" \ "$(INTDIR)\jsscript.obj" \ "$(INTDIR)\jsstr.obj" \ "$(INTDIR)\jsutil.obj" \ "$(INTDIR)\jsxdrapi.obj" \ "$(INTDIR)\jsxml.obj" \ "$(INTDIR)\prmjtime.obj" \ "$(OUTDIR)\fdlibm.lib" "$(OUTDIR)\js32.dll" : "$(OUTDIR)" $(DEF_FILE) $(LINK32_OBJS) $(LINK32) @<< $(LINK32_FLAGS) $(LINK32_OBJS) << !ELSEIF "$(CFG)" == "jskwgen - Win32 Release" # PROP BASE Use_MFC 0 # PROP BASE Use_Debug_Libraries 0 # PROP BASE Output_Dir "jsshell\Release" # PROP BASE Intermediate_Dir "jskwgen\Release" # PROP BASE Target_Dir "jskwgen" # PROP Use_MFC 0 # PROP Use_Debug_Libraries 0 # PROP Output_Dir "Release" # PROP Intermediate_Dir "Release" # PROP Target_Dir "jskwgen" OUTDIR=.\Release INTDIR=.\Release ALL : "$(INTDIR)" "$(INTDIR)\host_jskwgen.exe" CLEAN : -@erase "$(INTDIR)\jskwgen.obj" -@erase "$(INTDIR)\jskwgen.pch" -@erase "$(INTDIR)\host_jskwgen.exe" "$(INTDIR)" : if not exist "$(INTDIR)/$(NULL)" mkdir "$(INTDIR)" CPP=cl.exe # ADD BASE CPP /nologo /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_CONSOLE" /YX /c # ADD CPP /nologo /MD /W3 /GX /O2 /D "NDEBUG" /D "_CONSOLE" /D "WIN32" /D "XP_WIN" /D "JSFILE" /YX /c CPP_PROJ=/nologo /MD /W3 /GX /O2 /D "NDEBUG" /D "_CONSOLE" /D "WIN32" /D\ "XP_WIN" /D "JSFILE" /Fp"$(INTDIR)/jskwgen.pch" /YX /Fo"$(INTDIR)/" /c CPP_OBJS=.\Release/ CPP_SBRS=.\. .c{$(CPP_OBJS)}.obj: $(CPP) $(CPP_PROJ) $< .cpp{$(CPP_OBJS)}.obj: $(CPP) $(CPP_PROJ) $< .cxx{$(CPP_OBJS)}.obj: $(CPP) $(CPP_PROJ) $< .c{$(CPP_SBRS)}.sbr: $(CPP) $(CPP_PROJ) $< .cpp{$(CPP_SBRS)}.sbr: $(CPP) $(CPP_PROJ) $< .cxx{$(CPP_SBRS)}.sbr: $(CPP) $(CPP_PROJ) $< RSC=rc.exe # ADD BASE RSC /l 0x409 /d "NDEBUG" # ADD RSC /l 0x409 /d "NDEBUG" BSC32=bscmake.exe # ADD BASE BSC32 /nologo # ADD BSC32 /nologo BSC32_FLAGS=/nologo /o"$(INTDIR)/jskwgen.bsc" BSC32_SBRS= \ LINK32=link.exe # ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /machine:I386 # ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /machine:I386 LINK32_FLAGS=kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib\ advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib\ odbccp32.lib /nologo /subsystem:console /incremental:no\ /pdb:"$(INTDIR)/jskwgen.pdb" /machine:I386 /out:"$(INTDIR)/host_jskwgen.exe" LINK32_OBJS= \ "$(INTDIR)\jskwgen.obj" \ "$(INTDIR)\host_jskwgen.exe" : "$(INTDIR)" $(DEF_FILE) $(LINK32_OBJS) $(LINK32) @<< $(LINK32_FLAGS) $(LINK32_OBJS) << !ELSEIF "$(CFG)" == "jskwgen - Win32 Debug" # PROP BASE Use_MFC 0 # PROP BASE Use_Debug_Libraries 0 # PROP BASE Output_Dir "jsshell\Debug" # PROP BASE Intermediate_Dir "jskwgen\Debug" # PROP BASE Target_Dir "jskwgen" # PROP Use_MFC 0 # PROP Use_Debug_Libraries 0 # PROP Output_Dir "Debug" # PROP Intermediate_Dir "Debug" # PROP Target_Dir "jskwgen" OUTDIR=.\Debug INTDIR=.\Debug ALL : "$(INTDIR)" "$(INTDIR)\host_jskwgen.exe" CLEAN : -@erase "$(INTDIR)\jskwgen.obj" -@erase "$(INTDIR)\jskwgen.pch" -@erase "$(INTDIR)\host_jskwgen.exe" "$(INTDIR)" : if not exist "$(INTDIR)/$(NULL)" mkdir "$(INTDIR)" CPP=cl.exe # ADD BASE CPP /nologo /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_CONSOLE" /YX /c # ADD CPP /nologo /MD /W3 /GX /O2 /D "NDEBUG" /D "_CONSOLE" /D "WIN32" /D "XP_WIN" /D "JSFILE" /YX /c CPP_PROJ=/nologo /MD /W3 /GX /O2 /D "NDEBUG" /D "_CONSOLE" /D "WIN32" /D\ "XP_WIN" /D "JSFILE" /Fp"$(INTDIR)/jskwgen.pch" /YX /Fo"$(INTDIR)/" /c CPP_OBJS=.\Debug/ CPP_SBRS=.\. .c{$(CPP_OBJS)}.obj: $(CPP) $(CPP_PROJ) $< .cpp{$(CPP_OBJS)}.obj: $(CPP) $(CPP_PROJ) $< .cxx{$(CPP_OBJS)}.obj: $(CPP) $(CPP_PROJ) $< .c{$(CPP_SBRS)}.sbr: $(CPP) $(CPP_PROJ) $< .cpp{$(CPP_SBRS)}.sbr: $(CPP) $(CPP_PROJ) $< .cxx{$(CPP_SBRS)}.sbr: $(CPP) $(CPP_PROJ) $< RSC=rc.exe # ADD BASE RSC /l 0x409 /d "NDEBUG" # ADD RSC /l 0x409 /d "NDEBUG" BSC32=bscmake.exe # ADD BASE BSC32 /nologo # ADD BSC32 /nologo BSC32_FLAGS=/nologo /o"$(INTDIR)/jskwgen.bsc" BSC32_SBRS= \ LINK32=link.exe # ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /machine:I386 # ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /machine:I386 LINK32_FLAGS=kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib\ advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib\ odbccp32.lib /nologo /subsystem:console /incremental:no\ /pdb:"$(INTDIR)/jskwgen.pdb" /machine:I386 /out:"$(INTDIR)/host_jskwgen.exe" LINK32_OBJS= \ "$(INTDIR)\jskwgen.obj" \ "$(INTDIR)\host_jskwgen.exe" : "$(INTDIR)" $(DEF_FILE) $(LINK32_OBJS) $(LINK32) @<< $(LINK32_FLAGS) $(LINK32_OBJS) << !ELSEIF "$(CFG)" == "jsshell - Win32 Release" # PROP BASE Use_MFC 0 # PROP BASE Use_Debug_Libraries 0 # PROP BASE Output_Dir "jsshell\Release" # PROP BASE Intermediate_Dir "jsshell\Release" # PROP BASE Target_Dir "jsshell" # PROP Use_MFC 0 # PROP Use_Debug_Libraries 0 # PROP Output_Dir "Release" # PROP Intermediate_Dir "Release" # PROP Target_Dir "jsshell" OUTDIR=.\Release INTDIR=.\Release ALL : "js - Win32 Release" "$(OUTDIR)\jsshell.exe" CLEAN : -@erase "$(INTDIR)\js.obj" -@erase "$(INTDIR)\jsshell.pch" -@erase "$(OUTDIR)\jsshell.exe" -@$(MAKE) /nologo /$(MAKEFLAGS) /F ".\js.mak" CFG="js - Win32 Release" clean "$(OUTDIR)" : if not exist "$(OUTDIR)/$(NULL)" mkdir "$(OUTDIR)" CPP=cl.exe # ADD BASE CPP /nologo /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_CONSOLE" /YX /c # ADD CPP /nologo /MD /W3 /GX /O2 /D "NDEBUG" /D "_CONSOLE" /D "WIN32" /D "XP_WIN" /D "JSFILE" /I"$(INTDIR)" /YX /c CPP_PROJ=/nologo /MD /W3 /GX /O2 /D "NDEBUG" /D "_CONSOLE" /D "WIN32" /D\ "XP_WIN" /D "JSFILE" /Fp"$(INTDIR)/jsshell.pch" /I"$(INTDIR)" /YX /Fo"$(INTDIR)/" /c CPP_OBJS=.\Release/ CPP_SBRS=.\. .c{$(CPP_OBJS)}.obj: $(CPP) $(CPP_PROJ) $< .cpp{$(CPP_OBJS)}.obj: $(CPP) $(CPP_PROJ) $< .cxx{$(CPP_OBJS)}.obj: $(CPP) $(CPP_PROJ) $< .c{$(CPP_SBRS)}.sbr: $(CPP) $(CPP_PROJ) $< .cpp{$(CPP_SBRS)}.sbr: $(CPP) $(CPP_PROJ) $< .cxx{$(CPP_SBRS)}.sbr: $(CPP) $(CPP_PROJ) $< RSC=rc.exe # ADD BASE RSC /l 0x409 /d "NDEBUG" # ADD RSC /l 0x409 /d "NDEBUG" BSC32=bscmake.exe # ADD BASE BSC32 /nologo # ADD BSC32 /nologo BSC32_FLAGS=/nologo /o"$(OUTDIR)/jsshell.bsc" BSC32_SBRS= \ LINK32=link.exe # ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /machine:I386 # ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /machine:I386 LINK32_FLAGS=kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib\ advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib\ odbccp32.lib /nologo /subsystem:console /incremental:no\ /pdb:"$(OUTDIR)/jsshell.pdb" /machine:I386 /out:"$(OUTDIR)/jsshell.exe" LINK32_OBJS= \ "$(INTDIR)\js.obj" \ "$(OUTDIR)\js32.lib" "$(OUTDIR)\jsshell.exe" : "$(OUTDIR)" $(DEF_FILE) $(LINK32_OBJS) $(LINK32) @<< $(LINK32_FLAGS) $(LINK32_OBJS) << !ELSEIF "$(CFG)" == "jsshell - Win32 Debug" # PROP BASE Use_MFC 0 # PROP BASE Use_Debug_Libraries 1 # PROP BASE Output_Dir "jsshell\jsshell_" # PROP BASE Intermediate_Dir "jsshell\jsshell_" # PROP BASE Target_Dir "jsshell" # PROP Use_MFC 0 # PROP Use_Debug_Libraries 1 # PROP Output_Dir "Debug" # PROP Intermediate_Dir "Debug" # PROP Target_Dir "jsshell" OUTDIR=.\Debug INTDIR=.\Debug ALL : "js - Win32 Debug" "$(OUTDIR)\jsshell.exe" CLEAN : -@erase "$(INTDIR)\js.obj" -@erase "$(INTDIR)\jsshell.pch" -@erase "$(OUTDIR)\jsshell.exe" -@erase "$(OUTDIR)\jsshell.ilk" -@erase "$(OUTDIR)\jsshell.pdb" -@$(MAKE) /nologo /$(MAKEFLAGS) /F ".\js.mak" CFG="js - Win32 Debug" clean "$(OUTDIR)" : if not exist "$(OUTDIR)/$(NULL)" mkdir "$(OUTDIR)" CPP=cl.exe # ADD BASE CPP /nologo /W3 /Gm /GX /Zi /Od /D "WIN32" /D "_DEBUG" /D "_CONSOLE" /YX /c # ADD CPP /nologo /MDd /W3 /Gm /GX /Zi /Od /D "_CONSOLE" /D "_DEBUG" /D "WIN32" /D "XP_WIN" /D "JSFILE" /D "DEBUG" /YX /c CPP_PROJ=/nologo /MDd /W3 /Gm /GX /Zi /Od /D "_CONSOLE" /D "_DEBUG" /D "WIN32"\ /D "XP_WIN" /D "JSFILE" /D "DEBUG" /Fp"$(INTDIR)/jsshell.pch" /YX\ /Fo"$(INTDIR)/" /Fd"$(INTDIR)/" /c CPP_OBJS=.\Debug/ CPP_SBRS=.\. .c{$(CPP_OBJS)}.obj: $(CPP) $(CPP_PROJ) $< .cpp{$(CPP_OBJS)}.obj: $(CPP) $(CPP_PROJ) $< .cxx{$(CPP_OBJS)}.obj: $(CPP) $(CPP_PROJ) $< .c{$(CPP_SBRS)}.sbr: $(CPP) $(CPP_PROJ) $< .cpp{$(CPP_SBRS)}.sbr: $(CPP) $(CPP_PROJ) $< .cxx{$(CPP_SBRS)}.sbr: $(CPP) $(CPP_PROJ) $< RSC=rc.exe # ADD BASE RSC /l 0x409 /d "_DEBUG" # ADD RSC /l 0x409 /d "_DEBUG" BSC32=bscmake.exe # ADD BASE BSC32 /nologo # ADD BSC32 /nologo BSC32_FLAGS=/nologo /o"$(OUTDIR)/jsshell.bsc" BSC32_SBRS= \ LINK32=link.exe # ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /debug /machine:I386 # ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /debug /machine:I386 LINK32_FLAGS=kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib\ advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib\ odbccp32.lib /nologo /subsystem:console /incremental:yes\ /pdb:"$(OUTDIR)/jsshell.pdb" /debug /machine:I386 /out:"$(OUTDIR)/jsshell.exe" LINK32_OBJS= \ "$(INTDIR)\js.obj" \ "$(OUTDIR)\js32.lib" "$(OUTDIR)\jsshell.exe" : "$(OUTDIR)" $(DEF_FILE) $(LINK32_OBJS) $(LINK32) @<< $(LINK32_FLAGS) $(LINK32_OBJS) << !ELSEIF "$(CFG)" == "fdlibm - Win32 Release" # PROP BASE Use_MFC 0 # PROP BASE Use_Debug_Libraries 0 # PROP BASE Output_Dir "fdlibm\Release" # PROP BASE Intermediate_Dir "fdlibm\Release" # PROP BASE Target_Dir "fdlibm" # PROP Use_MFC 0 # PROP Use_Debug_Libraries 0 # PROP Output_Dir "Release" # PROP Intermediate_Dir "Release" # PROP Target_Dir "fdlibm" OUTDIR=.\Release INTDIR=.\Release ALL : "$(OUTDIR)\fdlibm.lib" CLEAN : -@erase "$(INTDIR)\e_atan2.obj" -@erase "$(INTDIR)\e_pow.obj" -@erase "$(INTDIR)\e_sqrt.obj" -@erase "$(INTDIR)\k_standard.obj" -@erase "$(INTDIR)\s_atan.obj" -@erase "$(INTDIR)\s_copysign.obj" -@erase "$(INTDIR)\s_fabs.obj" -@erase "$(INTDIR)\s_finite.obj" -@erase "$(INTDIR)\s_isnan.obj" -@erase "$(INTDIR)\s_matherr.obj" -@erase "$(INTDIR)\s_rint.obj" -@erase "$(INTDIR)\s_scalbn.obj" -@erase "$(INTDIR)\w_atan2.obj" -@erase "$(INTDIR)\w_pow.obj" -@erase "$(INTDIR)\w_sqrt.obj" -@erase "$(INTDIR)\fdlibm.pch" -@erase "$(OUTDIR)\fdlibm.lib" "$(OUTDIR)" : if not exist "$(OUTDIR)/$(NULL)" mkdir "$(OUTDIR)" CPP=cl.exe # ADD BASE CPP /nologo /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D _X86_=1 /D "_WINDOWS" /YX /c # ADD CPP /nologo /MD /W3 /GX /O2 /D "NDEBUG" /D "WIN32" /D _X86_=1 /D "_WINDOWS" /D "_IEEE_LIBM" /YX /c CPP_PROJ=/nologo /MD /W3 /GX /O2 /D "NDEBUG" /D "WIN32" /D _X86_=1 /D "_WINDOWS" /D\ "_IEEE_LIBM" /D "XP_WIN" /I .\ /Fp"$(INTDIR)/fdlibm.pch" /YX /Fo"$(INTDIR)/" /c CPP_OBJS=.\Release/ CPP_SBRS=.\. .c{$(CPP_OBJS)}.obj: $(CPP) $(CPP_PROJ) $< .cpp{$(CPP_OBJS)}.obj: $(CPP) $(CPP_PROJ) $< .cxx{$(CPP_OBJS)}.obj: $(CPP) $(CPP_PROJ) $< .c{$(CPP_SBRS)}.sbr: $(CPP) $(CPP_PROJ) $< .cpp{$(CPP_SBRS)}.sbr: $(CPP) $(CPP_PROJ) $< .cxx{$(CPP_SBRS)}.sbr: $(CPP) $(CPP_PROJ) $< BSC32=bscmake.exe # ADD BASE BSC32 /nologo # ADD BSC32 /nologo BSC32_FLAGS=/nologo /o"$(OUTDIR)/fdlibm.bsc" BSC32_SBRS= \ LIB32=link.exe -lib # ADD BASE LIB32 /nologo # ADD LIB32 /nologo LIB32_FLAGS=/nologo /out:"$(OUTDIR)/fdlibm.lib" LIB32_OBJS= \ "$(INTDIR)\e_atan2.obj" \ "$(INTDIR)\e_pow.obj" \ "$(INTDIR)\e_sqrt.obj" \ "$(INTDIR)\k_standard.obj" \ "$(INTDIR)\s_atan.obj" \ "$(INTDIR)\s_copysign.obj" \ "$(INTDIR)\s_fabs.obj" \ "$(INTDIR)\s_finite.obj" \ "$(INTDIR)\s_isnan.obj" \ "$(INTDIR)\s_matherr.obj" \ "$(INTDIR)\s_rint.obj" \ "$(INTDIR)\s_scalbn.obj" \ "$(INTDIR)\w_atan2.obj" \ "$(INTDIR)\w_pow.obj" \ "$(INTDIR)\w_sqrt.obj" "$(OUTDIR)\fdlibm.lib" : "$(OUTDIR)" $(DEF_FILE) $(LIB32_OBJS) $(LIB32) @<< $(LIB32_FLAGS) $(DEF_FLAGS) $(LIB32_OBJS) << !ELSEIF "$(CFG)" == "fdlibm - Win32 Debug" # PROP BASE Use_MFC 0 # PROP BASE Use_Debug_Libraries 1 # PROP BASE Output_Dir "fdlibm\Debug" # PROP BASE Intermediate_Dir "fdlibm\Debug" # PROP BASE Target_Dir "fdlibm" # PROP Use_MFC 0 # PROP Use_Debug_Libraries 1 # PROP Output_Dir "Debug" # PROP Intermediate_Dir "Debug" # PROP Target_Dir "fdlibm" OUTDIR=.\Debug INTDIR=.\Debug ALL : "$(OUTDIR)\fdlibm.lib" CLEAN : -@erase "$(INTDIR)\e_atan2.obj" -@erase "$(INTDIR)\e_pow.obj" -@erase "$(INTDIR)\e_sqrt.obj" -@erase "$(INTDIR)\k_standard.obj" -@erase "$(INTDIR)\s_atan.obj" -@erase "$(INTDIR)\s_copysign.obj" -@erase "$(INTDIR)\s_fabs.obj" -@erase "$(INTDIR)\s_finite.obj" -@erase "$(INTDIR)\s_isnan.obj" -@erase "$(INTDIR)\s_matherr.obj" -@erase "$(INTDIR)\s_rint.obj" -@erase "$(INTDIR)\s_scalbn.obj" -@erase "$(INTDIR)\w_atan2.obj" -@erase "$(INTDIR)\w_pow.obj" -@erase "$(INTDIR)\w_sqrt.obj" -@erase "$(INTDIR)\fdlibm.pch" -@erase "$(OUTDIR)\fdlibm.lib" "$(OUTDIR)" : if not exist "$(OUTDIR)/$(NULL)" mkdir "$(OUTDIR)" CPP=cl.exe # ADD BASE CPP /nologo /W3 /GX /Z7 /Od /D "WIN32" /D "_DEBUG" /D _X86_=1 /D "_WINDOWS" /YX /c # ADD CPP /nologo /MDd /W3 /GX /Z7 /Od /D "_DEBUG" /D "WIN32" /D _X86_=1 /D "_WINDOWS" /D "_IEEE_LIBM" /YX /c CPP_PROJ=/nologo /MDd /W3 /GX /Z7 /Od /D "_DEBUG" /D "WIN32" /D _X86_=1 /D "_WINDOWS" /D\ "_IEEE_LIBM" /D "XP_WIN" -I .\ /Fp"$(INTDIR)/fdlibm.pch" /YX /Fo"$(INTDIR)/" /c CPP_OBJS=.\Debug/ CPP_SBRS=.\. .c{$(CPP_OBJS)}.obj: $(CPP) $(CPP_PROJ) $< .cpp{$(CPP_OBJS)}.obj: $(CPP) $(CPP_PROJ) $< .cxx{$(CPP_OBJS)}.obj: $(CPP) $(CPP_PROJ) $< .c{$(CPP_SBRS)}.sbr: $(CPP) $(CPP_PROJ) $< .cpp{$(CPP_SBRS)}.sbr: $(CPP) $(CPP_PROJ) $< .cxx{$(CPP_SBRS)}.sbr: $(CPP) $(CPP_PROJ) $< BSC32=bscmake.exe # ADD BASE BSC32 /nologo # ADD BSC32 /nologo BSC32_FLAGS=/nologo /o"$(OUTDIR)/fdlibm.bsc" BSC32_SBRS= \ LIB32=link.exe -lib # ADD BASE LIB32 /nologo # ADD LIB32 /nologo LIB32_FLAGS=/nologo /out:"$(OUTDIR)/fdlibm.lib" LIB32_OBJS= \ "$(INTDIR)\e_atan2.obj" \ "$(INTDIR)\e_pow.obj" \ "$(INTDIR)\e_sqrt.obj" \ "$(INTDIR)\k_standard.obj" \ "$(INTDIR)\s_atan.obj" \ "$(INTDIR)\s_copysign.obj" \ "$(INTDIR)\s_fabs.obj" \ "$(INTDIR)\s_finite.obj" \ "$(INTDIR)\s_isnan.obj" \ "$(INTDIR)\s_matherr.obj" \ "$(INTDIR)\s_rint.obj" \ "$(INTDIR)\s_scalbn.obj" \ "$(INTDIR)\w_atan2.obj" \ "$(INTDIR)\w_pow.obj" \ "$(INTDIR)\w_sqrt.obj" "$(OUTDIR)\fdlibm.lib" : "$(OUTDIR)" $(DEF_FILE) $(LIB32_OBJS) $(LIB32) @<< $(LIB32_FLAGS) $(DEF_FLAGS) $(LIB32_OBJS) << !ENDIF ################################################################################ # Begin Target # Name "js - Win32 Release" # Name "js - Win32 Debug" !IF "$(CFG)" == "js - Win32 Release" !ELSEIF "$(CFG)" == "js - Win32 Debug" !ENDIF ################################################################################ # Begin Source File SOURCE=.\jsapi.c !IF "$(CFG)" == "js - Win32 Release" DEP_CPP_JSAPI=\ ".\js.msg"\ ".\jsapi.h"\ ".\jsarena.h"\ ".\jsarray.h"\ ".\jsatom.h"\ ".\jsbool.h"\ ".\jsclist.h"\ ".\jscntxt.h"\ ".\jscompat.h"\ ".\jsconfig.h"\ ".\jscpucfg.h"\ ".\jsdate.h"\ ".\jsemit.h"\ ".\jsexn.h"\ ".\jsfile.h"\ ".\jsfun.h"\ ".\jsgc.h"\ ".\jshash.h"\ ".\jsinterp.h"\ ".\jslock.h"\ ".\jslong.h"\ ".\jsmath.h"\ ".\jsnum.h"\ ".\jsobj.h"\ ".\jsopcode.h"\ ".\jsopcode.tbl"\ ".\jsosdep.h"\ ".\jsotypes.h"\ ".\jsparse.h"\ ".\jsprf.h"\ ".\jsprvtd.h"\ ".\jspubtd.h"\ ".\jsregexp.h"\ ".\jsscan.h"\ ".\jsscope.h"\ ".\jsscript.h"\ ".\jsstddef.h"\ ".\jsstr.h"\ ".\jstypes.h"\ ".\jsutil.h"\ ".\jsxml.h"\ {$(INCLUDE)}"\sys\types.h"\ NODEP_CPP_JSAPI=\ ".\jsautocfg.h"\ ".\prcvar.h"\ ".\prlock.h"\ "$(INTDIR)\jsapi.obj" : $(SOURCE) $(DEP_CPP_JSAPI) "$(INTDIR)" !ELSEIF "$(CFG)" == "js - Win32 Debug" DEP_CPP_JSAPI=\ ".\js.msg"\ ".\jsapi.h"\ ".\jsarena.h"\ ".\jsarray.h"\ ".\jsatom.h"\ ".\jsbool.h"\ ".\jsclist.h"\ ".\jscntxt.h"\ ".\jscompat.h"\ ".\jsconfig.h"\ ".\jscpucfg.h"\ ".\jsdate.h"\ ".\jsemit.h"\ ".\jsexn.h"\ ".\jsfile.h"\ ".\jsfun.h"\ ".\jsgc.h"\ ".\jshash.h"\ ".\jsinterp.h"\ ".\jslock.h"\ ".\jslong.h"\ ".\jsmath.h"\ ".\jsnum.h"\ ".\jsobj.h"\ ".\jsopcode.h"\ ".\jsopcode.tbl"\ ".\jsosdep.h"\ ".\jsotypes.h"\ ".\jsparse.h"\ ".\jsprf.h"\ ".\jsprvtd.h"\ ".\jspubtd.h"\ ".\jsregexp.h"\ ".\jsscan.h"\ ".\jsscope.h"\ ".\jsscript.h"\ ".\jsstddef.h"\ ".\jsstr.h"\ ".\jstypes.h"\ ".\jsutil.h"\ ".\jsxml.h"\ {$(INCLUDE)}"\sys\types.h"\ NODEP_CPP_JSAPI=\ ".\jsautocfg.h"\ ".\prcvar.h"\ ".\prlock.h"\ "$(INTDIR)\jsapi.obj" : $(SOURCE) $(DEP_CPP_JSAPI) "$(INTDIR)" !ENDIF # End Source File ################################################################################ # Begin Source File SOURCE=.\jsarena.c !IF "$(CFG)" == "js - Win32 Release" DEP_CPP_JSARE=\ ".\jsarena.h"\ ".\jsbit.h"\ ".\jscompat.h"\ ".\jscpucfg.h"\ ".\jslong.h"\ ".\jsosdep.h"\ ".\jsotypes.h"\ ".\jsstddef.h"\ ".\jstypes.h"\ ".\jsutil.h"\ {$(INCLUDE)}"\sys\types.h"\ NODEP_CPP_JSARE=\ ".\jsautocfg.h"\ "$(INTDIR)\jsarena.obj" : $(SOURCE) $(DEP_CPP_JSARE) "$(INTDIR)" !ELSEIF "$(CFG)" == "js - Win32 Debug" DEP_CPP_JSARE=\ ".\jsarena.h"\ ".\jsbit.h"\ ".\jscompat.h"\ ".\jscpucfg.h"\ ".\jslong.h"\ ".\jsosdep.h"\ ".\jsotypes.h"\ ".\jsstddef.h"\ ".\jstypes.h"\ ".\jsutil.h"\ {$(INCLUDE)}"\sys\types.h"\ NODEP_CPP_JSARE=\ ".\jsautocfg.h"\ "$(INTDIR)\jsarena.obj" : $(SOURCE) $(DEP_CPP_JSARE) "$(INTDIR)" !ENDIF # End Source File ################################################################################ # Begin Source File SOURCE=.\jsarray.c !IF "$(CFG)" == "js - Win32 Release" DEP_CPP_JSARR=\ ".\js.msg"\ ".\jsapi.h"\ ".\jsarena.h"\ ".\jsarray.h"\ ".\jsatom.h"\ ".\jsclist.h"\ ".\jscntxt.h"\ ".\jscompat.h"\ ".\jsconfig.h"\ ".\jscpucfg.h"\ ".\jsfun.h"\ ".\jsgc.h"\ ".\jshash.h"\ ".\jsinterp.h"\ ".\jslock.h"\ ".\jslong.h"\ ".\jsnum.h"\ ".\jsobj.h"\ ".\jsosdep.h"\ ".\jsotypes.h"\ ".\jsprvtd.h"\ ".\jspubtd.h"\ ".\jsregexp.h"\ ".\jsscope.h"\ ".\jsstddef.h"\ ".\jsstr.h"\ ".\jstypes.h"\ ".\jsutil.h"\ {$(INCLUDE)}"\sys\types.h"\ NODEP_CPP_JSARR=\ ".\jsautocfg.h"\ ".\prcvar.h"\ ".\prlock.h"\ "$(INTDIR)\jsarray.obj" : $(SOURCE) $(DEP_CPP_JSARR) "$(INTDIR)" !ELSEIF "$(CFG)" == "js - Win32 Debug" DEP_CPP_JSARR=\ ".\js.msg"\ ".\jsapi.h"\ ".\jsarena.h"\ ".\jsarray.h"\ ".\jsatom.h"\ ".\jsclist.h"\ ".\jscntxt.h"\ ".\jscompat.h"\ ".\jsconfig.h"\ ".\jscpucfg.h"\ ".\jsfun.h"\ ".\jsgc.h"\ ".\jshash.h"\ ".\jsinterp.h"\ ".\jslock.h"\ ".\jslong.h"\ ".\jsnum.h"\ ".\jsobj.h"\ ".\jsosdep.h"\ ".\jsotypes.h"\ ".\jsprvtd.h"\ ".\jspubtd.h"\ ".\jsregexp.h"\ ".\jsscope.h"\ ".\jsstddef.h"\ ".\jsstr.h"\ ".\jstypes.h"\ ".\jsutil.h"\ {$(INCLUDE)}"\sys\types.h"\ NODEP_CPP_JSARR=\ ".\jsautocfg.h"\ ".\prcvar.h"\ ".\prlock.h"\ "$(INTDIR)\jsarray.obj" : $(SOURCE) $(DEP_CPP_JSARR) "$(INTDIR)" !ENDIF # End Source File ################################################################################ # Begin Source File SOURCE=.\jsatom.c !IF "$(CFG)" == "js - Win32 Release" DEP_CPP_JSATO=\ ".\js.msg"\ ".\jsapi.h"\ ".\jsarena.h"\ ".\jsatom.h"\ ".\jsclist.h"\ ".\jscntxt.h"\ ".\jscompat.h"\ ".\jsconfig.h"\ ".\jscpucfg.h"\ ".\jsgc.h"\ ".\jshash.h"\ ".\jsinterp.h"\ ".\jslock.h"\ ".\jslong.h"\ ".\jsnum.h"\ ".\jsobj.h"\ ".\jsopcode.h"\ ".\jsopcode.tbl"\ ".\jsosdep.h"\ ".\jsotypes.h"\ ".\jsprf.h"\ ".\jsprvtd.h"\ ".\jspubtd.h"\ ".\jsregexp.h"\ ".\jsscope.h"\ ".\jsstddef.h"\ ".\jsstr.h"\ ".\jstypes.h"\ ".\jsutil.h"\ {$(INCLUDE)}"\sys\types.h"\ NODEP_CPP_JSATO=\ ".\jsautocfg.h"\ ".\prcvar.h"\ ".\prlock.h"\ "$(INTDIR)\jsatom.obj" : $(SOURCE) $(DEP_CPP_JSATO) "$(INTDIR)" !ELSEIF "$(CFG)" == "js - Win32 Debug" DEP_CPP_JSATO=\ ".\js.msg"\ ".\jsapi.h"\ ".\jsarena.h"\ ".\jsatom.h"\ ".\jsclist.h"\ ".\jscntxt.h"\ ".\jscompat.h"\ ".\jsconfig.h"\ ".\jscpucfg.h"\ ".\jsgc.h"\ ".\jshash.h"\ ".\jsinterp.h"\ ".\jslock.h"\ ".\jslong.h"\ ".\jsnum.h"\ ".\jsobj.h"\ ".\jsopcode.h"\ ".\jsopcode.tbl"\ ".\jsosdep.h"\ ".\jsotypes.h"\ ".\jsprf.h"\ ".\jsprvtd.h"\ ".\jspubtd.h"\ ".\jsregexp.h"\ ".\jsscope.h"\ ".\jsstddef.h"\ ".\jsstr.h"\ ".\jstypes.h"\ ".\jsutil.h"\ {$(INCLUDE)}"\sys\types.h"\ NODEP_CPP_JSATO=\ ".\jsautocfg.h"\ ".\prcvar.h"\ ".\prlock.h"\ "$(INTDIR)\jsatom.obj" : $(SOURCE) $(DEP_CPP_JSATO) "$(INTDIR)" !ENDIF # End Source File ################################################################################ # Begin Source File SOURCE=.\jsbool.c !IF "$(CFG)" == "js - Win32 Release" DEP_CPP_JSBOO=\ ".\js.msg"\ ".\jsapi.h"\ ".\jsarena.h"\ ".\jsatom.h"\ ".\jsbool.h"\ ".\jsclist.h"\ ".\jscntxt.h"\ ".\jscompat.h"\ ".\jsconfig.h"\ ".\jscpucfg.h"\ ".\jsgc.h"\ ".\jshash.h"\ ".\jsinterp.h"\ ".\jslock.h"\ ".\jslong.h"\ ".\jsnum.h"\ ".\jsobj.h"\ ".\jsosdep.h"\ ".\jsotypes.h"\ ".\jsprf.h"\ ".\jsprvtd.h"\ ".\jspubtd.h"\ ".\jsregexp.h"\ ".\jsscope.h"\ ".\jsstddef.h"\ ".\jsstr.h"\ ".\jstypes.h"\ ".\jsutil.h"\ {$(INCLUDE)}"\sys\types.h"\ NODEP_CPP_JSBOO=\ ".\jsautocfg.h"\ ".\prcvar.h"\ ".\prlock.h"\ "$(INTDIR)\jsbool.obj" : $(SOURCE) $(DEP_CPP_JSBOO) "$(INTDIR)" !ELSEIF "$(CFG)" == "js - Win32 Debug" DEP_CPP_JSBOO=\ ".\js.msg"\ ".\jsapi.h"\ ".\jsarena.h"\ ".\jsatom.h"\ ".\jsbool.h"\ ".\jsclist.h"\ ".\jscntxt.h"\ ".\jscompat.h"\ ".\jsconfig.h"\ ".\jscpucfg.h"\ ".\jsgc.h"\ ".\jshash.h"\ ".\jsinterp.h"\ ".\jslock.h"\ ".\jslong.h"\ ".\jsnum.h"\ ".\jsobj.h"\ ".\jsosdep.h"\ ".\jsotypes.h"\ ".\jsprf.h"\ ".\jsprvtd.h"\ ".\jspubtd.h"\ ".\jsregexp.h"\ ".\jsscope.h"\ ".\jsstddef.h"\ ".\jsstr.h"\ ".\jstypes.h"\ ".\jsutil.h"\ {$(INCLUDE)}"\sys\types.h"\ NODEP_CPP_JSBOO=\ ".\jsautocfg.h"\ ".\prcvar.h"\ ".\prlock.h"\ "$(INTDIR)\jsbool.obj" : $(SOURCE) $(DEP_CPP_JSBOO) "$(INTDIR)" !ENDIF # End Source File ################################################################################ # Begin Source File SOURCE=.\jscntxt.c !IF "$(CFG)" == "js - Win32 Release" DEP_CPP_JSCNT=\ ".\js.msg"\ ".\jsapi.h"\ ".\jsarena.h"\ ".\jsatom.h"\ ".\jsclist.h"\ ".\jscntxt.h"\ ".\jscompat.h"\ ".\jsconfig.h"\ ".\jscpucfg.h"\ ".\jsdbgapi.h"\ ".\jsexn.h"\ ".\jsgc.h"\ ".\jshash.h"\ ".\jsinterp.h"\ ".\jslock.h"\ ".\jslong.h"\ ".\jsobj.h"\ ".\jsopcode.h"\ ".\jsopcode.tbl"\ ".\jsosdep.h"\ ".\jsotypes.h"\ ".\jsprf.h"\ ".\jsprvtd.h"\ ".\jspubtd.h"\ ".\jsregexp.h"\ ".\jsscan.h"\ ".\jsscope.h"\ ".\jsscript.h"\ ".\jsstddef.h"\ ".\jsstr.h"\ ".\jstypes.h"\ ".\jsutil.h"\ {$(INCLUDE)}"\sys\types.h"\ NODEP_CPP_JSCNT=\ ".\jsautocfg.h"\ ".\prcvar.h"\ ".\prlock.h"\ "$(INTDIR)\jscntxt.obj" : $(SOURCE) $(DEP_CPP_JSCNT) "$(INTDIR)" !ELSEIF "$(CFG)" == "js - Win32 Debug" DEP_CPP_JSCNT=\ ".\js.msg"\ ".\jsapi.h"\ ".\jsarena.h"\ ".\jsatom.h"\ ".\jsclist.h"\ ".\jscntxt.h"\ ".\jscompat.h"\ ".\jsconfig.h"\ ".\jscpucfg.h"\ ".\jsdbgapi.h"\ ".\jsexn.h"\ ".\jsgc.h"\ ".\jshash.h"\ ".\jsinterp.h"\ ".\jslock.h"\ ".\jslong.h"\ ".\jsobj.h"\ ".\jsopcode.h"\ ".\jsopcode.tbl"\ ".\jsosdep.h"\ ".\jsotypes.h"\ ".\jsprf.h"\ ".\jsprvtd.h"\ ".\jspubtd.h"\ ".\jsregexp.h"\ ".\jsscan.h"\ ".\jsscope.h"\ ".\jsscript.h"\ ".\jsstddef.h"\ ".\jsstr.h"\ ".\jstypes.h"\ ".\jsutil.h"\ {$(INCLUDE)}"\sys\types.h"\ NODEP_CPP_JSCNT=\ ".\jsautocfg.h"\ ".\prcvar.h"\ ".\prlock.h"\ "$(INTDIR)\jscntxt.obj" : $(SOURCE) $(DEP_CPP_JSCNT) "$(INTDIR)" !ENDIF # End Source File ################################################################################ # Begin Source File SOURCE=.\jsdate.c !IF "$(CFG)" == "js - Win32 Release" DEP_CPP_JSDAT=\ ".\js.msg"\ ".\jsapi.h"\ ".\jsarena.h"\ ".\jsatom.h"\ ".\jsclist.h"\ ".\jscntxt.h"\ ".\jscompat.h"\ ".\jsconfig.h"\ ".\jscpucfg.h"\ ".\jsdate.h"\ ".\jsdtoa.h"\ ".\jsgc.h"\ ".\jshash.h"\ ".\jsinterp.h"\ ".\jslock.h"\ ".\jslong.h"\ ".\jsnum.h"\ ".\jsobj.h"\ ".\jsosdep.h"\ ".\jsotypes.h"\ ".\jsprf.h"\ ".\jsprvtd.h"\ ".\jspubtd.h"\ ".\jsregexp.h"\ ".\jsscope.h"\ ".\jsstddef.h"\ ".\jsstr.h"\ ".\jstypes.h"\ ".\jsutil.h"\ ".\prmjtime.h"\ {$(INCLUDE)}"\sys\types.h"\ NODEP_CPP_JSDAT=\ ".\jsautocfg.h"\ ".\prcvar.h"\ ".\prlock.h"\ "$(INTDIR)\jsdate.obj" : $(SOURCE) $(DEP_CPP_JSDAT) "$(INTDIR)" !ELSEIF "$(CFG)" == "js - Win32 Debug" DEP_CPP_JSDAT=\ ".\js.msg"\ ".\jsapi.h"\ ".\jsarena.h"\ ".\jsatom.h"\ ".\jsclist.h"\ ".\jscntxt.h"\ ".\jscompat.h"\ ".\jsconfig.h"\ ".\jscpucfg.h"\ ".\jsdate.h"\ ".\jsdtoa.h"\ ".\jsgc.h"\ ".\jshash.h"\ ".\jsinterp.h"\ ".\jslock.h"\ ".\jslong.h"\ ".\jsnum.h"\ ".\jsobj.h"\ ".\jsosdep.h"\ ".\jsotypes.h"\ ".\jsprf.h"\ ".\jsprvtd.h"\ ".\jspubtd.h"\ ".\jsregexp.h"\ ".\jsscope.h"\ ".\jsstddef.h"\ ".\jsstr.h"\ ".\jstypes.h"\ ".\jsutil.h"\ ".\prmjtime.h"\ {$(INCLUDE)}"\sys\types.h"\ NODEP_CPP_JSDAT=\ ".\jsautocfg.h"\ ".\prcvar.h"\ ".\prlock.h"\ "$(INTDIR)\jsdate.obj" : $(SOURCE) $(DEP_CPP_JSDAT) "$(INTDIR)" !ENDIF # End Source File ################################################################################ # Begin Source File SOURCE=.\jsdbgapi.c !IF "$(CFG)" == "js - Win32 Release" DEP_CPP_JSDBG=\ ".\js.msg"\ ".\jsapi.h"\ ".\jsarena.h"\ ".\jsatom.h"\ ".\jsclist.h"\ ".\jscntxt.h"\ ".\jscompat.h"\ ".\jsconfig.h"\ ".\jscpucfg.h"\ ".\jsdbgapi.h"\ ".\jsfun.h"\ ".\jsgc.h"\ ".\jshash.h"\ ".\jsinterp.h"\ ".\jslock.h"\ ".\jslong.h"\ ".\jsobj.h"\ ".\jsopcode.h"\ ".\jsopcode.tbl"\ ".\jsosdep.h"\ ".\jsotypes.h"\ ".\jsprvtd.h"\ ".\jspubtd.h"\ ".\jsregexp.h"\ ".\jsscope.h"\ ".\jsscript.h"\ ".\jsstddef.h"\ ".\jsstr.h"\ ".\jstypes.h"\ ".\jsutil.h"\ {$(INCLUDE)}"\sys\types.h"\ NODEP_CPP_JSDBG=\ ".\jsautocfg.h"\ ".\prcvar.h"\ ".\prlock.h"\ "$(INTDIR)\jsdbgapi.obj" : $(SOURCE) $(DEP_CPP_JSDBG) "$(INTDIR)" !ELSEIF "$(CFG)" == "js - Win32 Debug" DEP_CPP_JSDBG=\ ".\js.msg"\ ".\jsapi.h"\ ".\jsarena.h"\ ".\jsatom.h"\ ".\jsclist.h"\ ".\jscntxt.h"\ ".\jscompat.h"\ ".\jsconfig.h"\ ".\jscpucfg.h"\ ".\jsdbgapi.h"\ ".\jsfun.h"\ ".\jsgc.h"\ ".\jshash.h"\ ".\jsinterp.h"\ ".\jslock.h"\ ".\jslong.h"\ ".\jsobj.h"\ ".\jsopcode.h"\ ".\jsopcode.tbl"\ ".\jsosdep.h"\ ".\jsotypes.h"\ ".\jsprvtd.h"\ ".\jspubtd.h"\ ".\jsregexp.h"\ ".\jsscope.h"\ ".\jsscript.h"\ ".\jsstddef.h"\ ".\jsstr.h"\ ".\jstypes.h"\ ".\jsutil.h"\ {$(INCLUDE)}"\sys\types.h"\ NODEP_CPP_JSDBG=\ ".\jsautocfg.h"\ ".\prcvar.h"\ ".\prlock.h"\ "$(INTDIR)\jsdbgapi.obj" : $(SOURCE) $(DEP_CPP_JSDBG) "$(INTDIR)" !ENDIF # End Source File ################################################################################ # Begin Source File SOURCE=.\jsdhash.c !IF "$(CFG)" == "js - Win32 Release" DEP_CPP_JSDHA=\ ".\jsbit.h"\ ".\jscompat.h"\ ".\jscpucfg.h"\ ".\jsdhash.h"\ ".\jslong.h"\ ".\jsosdep.h"\ ".\jsotypes.h"\ ".\jstypes.h"\ ".\jsutil.h"\ {$(INCLUDE)}"\sys\types.h"\ NODEP_CPP_JSDHA=\ ".\jsautocfg.h"\ "$(INTDIR)\jsdhash.obj" : $(SOURCE) $(DEP_CPP_JSDHA) "$(INTDIR)" !ELSEIF "$(CFG)" == "js - Win32 Debug" DEP_CPP_JSDHA=\ ".\jsbit.h"\ ".\jscompat.h"\ ".\jscpucfg.h"\ ".\jsdhash.h"\ ".\jslong.h"\ ".\jsosdep.h"\ ".\jsotypes.h"\ ".\jstypes.h"\ ".\jsutil.h"\ {$(INCLUDE)}"\sys\types.h"\ NODEP_CPP_JSDHA=\ ".\jsautocfg.h"\ "$(INTDIR)\jsdhash.obj" : $(SOURCE) $(DEP_CPP_JSDHA) "$(INTDIR)" !ENDIF # End Source File ################################################################################ # Begin Source File SOURCE=.\jsdtoa.c !IF "$(CFG)" == "js - Win32 Release" DEP_CPP_JSDTO=\ ".\jscompat.h"\ ".\jscpucfg.h"\ ".\jsdtoa.h"\ ".\jslong.h"\ ".\jsosdep.h"\ ".\jsotypes.h"\ ".\jsprf.h"\ ".\jsstddef.h"\ ".\jstypes.h"\ ".\jsutil.h"\ {$(INCLUDE)}"\sys\types.h"\ NODEP_CPP_JSDTO=\ ".\jsautocfg.h"\ ".\prlock.h"\ "$(INTDIR)\jsdtoa.obj" : $(SOURCE) $(DEP_CPP_JSDTO) "$(INTDIR)" !ELSEIF "$(CFG)" == "js - Win32 Debug" DEP_CPP_JSDTO=\ ".\jscompat.h"\ ".\jscpucfg.h"\ ".\jsdtoa.h"\ ".\jslong.h"\ ".\jsosdep.h"\ ".\jsotypes.h"\ ".\jsprf.h"\ ".\jsstddef.h"\ ".\jstypes.h"\ ".\jsutil.h"\ {$(INCLUDE)}"\sys\types.h"\ NODEP_CPP_JSDTO=\ ".\jsautocfg.h"\ ".\prlock.h"\ "$(INTDIR)\jsdtoa.obj" : $(SOURCE) $(DEP_CPP_JSDTO) "$(INTDIR)" !ENDIF # End Source File ################################################################################ # Begin Source File SOURCE=.\jsemit.c !IF "$(CFG)" == "js - Win32 Release" DEP_CPP_JSEMI=\ ".\js.msg"\ ".\jsapi.h"\ ".\jsarena.h"\ ".\jsatom.h"\ ".\jsclist.h"\ ".\jscntxt.h"\ ".\jscompat.h"\ ".\jsconfig.h"\ ".\jscpucfg.h"\ ".\jsemit.h"\ ".\jsfun.h"\ ".\jsgc.h"\ ".\jshash.h"\ ".\jsinterp.h"\ ".\jslock.h"\ ".\jslong.h"\ ".\jsnum.h"\ ".\jsobj.h"\ ".\jsopcode.h"\ ".\jsopcode.tbl"\ ".\jsosdep.h"\ ".\jsotypes.h"\ ".\jsparse.h"\ ".\jsprf.h"\ ".\jsprvtd.h"\ ".\jspubtd.h"\ ".\jsregexp.h"\ ".\jsscan.h"\ ".\jsscope.h"\ ".\jsscript.h"\ ".\jsstddef.h"\ ".\jsstr.h"\ ".\jstypes.h"\ ".\jsutil.h"\ {$(INCLUDE)}"\sys\types.h"\ NODEP_CPP_JSEMI=\ ".\jsautocfg.h"\ ".\prcvar.h"\ ".\prlock.h"\ "$(INTDIR)\jsemit.obj" : $(SOURCE) $(DEP_CPP_JSEMI) "$(INTDIR)" !ELSEIF "$(CFG)" == "js - Win32 Debug" DEP_CPP_JSEMI=\ ".\js.msg"\ ".\jsapi.h"\ ".\jsarena.h"\ ".\jsatom.h"\ ".\jsclist.h"\ ".\jscntxt.h"\ ".\jscompat.h"\ ".\jsconfig.h"\ ".\jscpucfg.h"\ ".\jsemit.h"\ ".\jsfun.h"\ ".\jsgc.h"\ ".\jshash.h"\ ".\jsinterp.h"\ ".\jslock.h"\ ".\jslong.h"\ ".\jsnum.h"\ ".\jsobj.h"\ ".\jsopcode.h"\ ".\jsopcode.tbl"\ ".\jsosdep.h"\ ".\jsotypes.h"\ ".\jsparse.h"\ ".\jsprf.h"\ ".\jsprvtd.h"\ ".\jspubtd.h"\ ".\jsregexp.h"\ ".\jsscan.h"\ ".\jsscope.h"\ ".\jsscript.h"\ ".\jsstddef.h"\ ".\jsstr.h"\ ".\jstypes.h"\ ".\jsutil.h"\ {$(INCLUDE)}"\sys\types.h"\ NODEP_CPP_JSEMI=\ ".\jsautocfg.h"\ ".\prcvar.h"\ ".\prlock.h"\ "$(INTDIR)\jsemit.obj" : $(SOURCE) $(DEP_CPP_JSEMI) "$(INTDIR)" !ENDIF # End Source File ################################################################################ # Begin Source File SOURCE=.\jsexn.c !IF "$(CFG)" == "js - Win32 Release" DEP_CPP_JSEXN=\ ".\js.msg"\ ".\jsapi.h"\ ".\jsarena.h"\ ".\jsatom.h"\ ".\jsclist.h"\ ".\jscntxt.h"\ ".\jscompat.h"\ ".\jsconfig.h"\ ".\jscpucfg.h"\ ".\jsexn.h"\ ".\jsfun.h"\ ".\jsgc.h"\ ".\jshash.h"\ ".\jsinterp.h"\ ".\jslock.h"\ ".\jslong.h"\ ".\jsobj.h"\ ".\jsosdep.h"\ ".\jsotypes.h"\ ".\jsprf.h"\ ".\jsprvtd.h"\ ".\jspubtd.h"\ ".\jsregexp.h"\ ".\jsscope.h"\ ".\jsstddef.h"\ ".\jsstr.h"\ ".\jstypes.h"\ ".\jsutil.h"\ {$(INCLUDE)}"\sys\types.h"\ NODEP_CPP_JSEXN=\ ".\jsautocfg.h"\ ".\prcvar.h"\ ".\prlock.h"\ "$(INTDIR)\jsexn.obj" : $(SOURCE) $(DEP_CPP_JSEXN) "$(INTDIR)" !ELSEIF "$(CFG)" == "js - Win32 Debug" DEP_CPP_JSEXN=\ ".\js.msg"\ ".\jsapi.h"\ ".\jsarena.h"\ ".\jsatom.h"\ ".\jsclist.h"\ ".\jscntxt.h"\ ".\jscompat.h"\ ".\jsconfig.h"\ ".\jscpucfg.h"\ ".\jsexn.h"\ ".\jsfun.h"\ ".\jsgc.h"\ ".\jshash.h"\ ".\jsinterp.h"\ ".\jslock.h"\ ".\jslong.h"\ ".\jsobj.h"\ ".\jsosdep.h"\ ".\jsotypes.h"\ ".\jsprf.h"\ ".\jsprvtd.h"\ ".\jspubtd.h"\ ".\jsregexp.h"\ ".\jsscope.h"\ ".\jsstddef.h"\ ".\jsstr.h"\ ".\jstypes.h"\ ".\jsutil.h"\ {$(INCLUDE)}"\sys\types.h"\ NODEP_CPP_JSEXN=\ ".\jsautocfg.h"\ ".\prcvar.h"\ ".\prlock.h"\ "$(INTDIR)\jsexn.obj" : $(SOURCE) $(DEP_CPP_JSEXN) "$(INTDIR)" !ENDIF # End Source File ################################################################################ # Begin Source File SOURCE=.\jsfun.c !IF "$(CFG)" == "js - Win32 Release" DEP_CPP_JSFUN=\ ".\js.msg"\ ".\jsapi.h"\ ".\jsarena.h"\ ".\jsarray.h"\ ".\jsatom.h"\ ".\jsclist.h"\ ".\jscntxt.h"\ ".\jscompat.h"\ ".\jsconfig.h"\ ".\jscpucfg.h"\ ".\jsfun.h"\ ".\jsgc.h"\ ".\jshash.h"\ ".\jsinterp.h"\ ".\jslock.h"\ ".\jslong.h"\ ".\jsnum.h"\ ".\jsobj.h"\ ".\jsopcode.h"\ ".\jsopcode.tbl"\ ".\jsosdep.h"\ ".\jsotypes.h"\ ".\jsparse.h"\ ".\jsprvtd.h"\ ".\jspubtd.h"\ ".\jsregexp.h"\ ".\jsscan.h"\ ".\jsscope.h"\ ".\jsscript.h"\ ".\jsstddef.h"\ ".\jsstr.h"\ ".\jstypes.h"\ ".\jsutil.h"\ ".\jsxdrapi.h"\ {$(INCLUDE)}"\sys\types.h"\ NODEP_CPP_JSFUN=\ ".\jsautocfg.h"\ ".\prcvar.h"\ ".\prlock.h"\ "$(INTDIR)\jsfun.obj" : $(SOURCE) $(DEP_CPP_JSFUN) "$(INTDIR)" !ELSEIF "$(CFG)" == "js - Win32 Debug" DEP_CPP_JSFUN=\ ".\js.msg"\ ".\jsapi.h"\ ".\jsarena.h"\ ".\jsarray.h"\ ".\jsatom.h"\ ".\jsclist.h"\ ".\jscntxt.h"\ ".\jscompat.h"\ ".\jsconfig.h"\ ".\jscpucfg.h"\ ".\jsfun.h"\ ".\jsgc.h"\ ".\jshash.h"\ ".\jsinterp.h"\ ".\jslock.h"\ ".\jslong.h"\ ".\jsnum.h"\ ".\jsobj.h"\ ".\jsopcode.h"\ ".\jsopcode.tbl"\ ".\jsosdep.h"\ ".\jsotypes.h"\ ".\jsparse.h"\ ".\jsprvtd.h"\ ".\jspubtd.h"\ ".\jsregexp.h"\ ".\jsscan.h"\ ".\jsscope.h"\ ".\jsscript.h"\ ".\jsstddef.h"\ ".\jsstr.h"\ ".\jstypes.h"\ ".\jsutil.h"\ ".\jsxdrapi.h"\ {$(INCLUDE)}"\sys\types.h"\ NODEP_CPP_JSFUN=\ ".\jsautocfg.h"\ ".\prcvar.h"\ ".\prlock.h"\ "$(INTDIR)\jsfun.obj" : $(SOURCE) $(DEP_CPP_JSFUN) "$(INTDIR)" !ENDIF # End Source File ################################################################################ # Begin Source File SOURCE=.\jsgc.c !IF "$(CFG)" == "js - Win32 Release" DEP_CPP_JSGC_=\ ".\js.msg"\ ".\jsapi.h"\ ".\jsarena.h"\ ".\jsatom.h"\ ".\jsclist.h"\ ".\jscntxt.h"\ ".\jscompat.h"\ ".\jsconfig.h"\ ".\jscpucfg.h"\ ".\jsfun.h"\ ".\jsgc.h"\ ".\jshash.h"\ ".\jsinterp.h"\ ".\jslock.h"\ ".\jslong.h"\ ".\jsnum.h"\ ".\jsobj.h"\ ".\jsosdep.h"\ ".\jsotypes.h"\ ".\jsprf.h"\ ".\jsprvtd.h"\ ".\jspubtd.h"\ ".\jsregexp.h"\ ".\jsscope.h"\ ".\jsscript.h"\ ".\jsstddef.h"\ ".\jsstr.h"\ ".\jstypes.h"\ ".\jsutil.h"\ ".\jsxml.h"\ {$(INCLUDE)}"\sys\types.h"\ NODEP_CPP_JSGC_=\ ".\jsautocfg.h"\ ".\prcvar.h"\ ".\prlock.h"\ "$(INTDIR)\jsgc.obj" : $(SOURCE) $(DEP_CPP_JSGC_) "$(INTDIR)" !ELSEIF "$(CFG)" == "js - Win32 Debug" DEP_CPP_JSGC_=\ ".\js.msg"\ ".\jsapi.h"\ ".\jsarena.h"\ ".\jsatom.h"\ ".\jsclist.h"\ ".\jscntxt.h"\ ".\jscompat.h"\ ".\jsconfig.h"\ ".\jscpucfg.h"\ ".\jsfun.h"\ ".\jsgc.h"\ ".\jshash.h"\ ".\jsinterp.h"\ ".\jslock.h"\ ".\jslong.h"\ ".\jsnum.h"\ ".\jsobj.h"\ ".\jsosdep.h"\ ".\jsotypes.h"\ ".\jsprf.h"\ ".\jsprvtd.h"\ ".\jspubtd.h"\ ".\jsregexp.h"\ ".\jsscope.h"\ ".\jsscript.h"\ ".\jsstddef.h"\ ".\jsstr.h"\ ".\jstypes.h"\ ".\jsutil.h"\ ".\jsxml.h"\ {$(INCLUDE)}"\sys\types.h"\ NODEP_CPP_JSGC_=\ ".\jsautocfg.h"\ ".\prcvar.h"\ ".\prlock.h"\ "$(INTDIR)\jsgc.obj" : $(SOURCE) $(DEP_CPP_JSGC_) "$(INTDIR)" !ENDIF # End Source File ################################################################################ # Begin Source File SOURCE=.\jshash.c !IF "$(CFG)" == "js - Win32 Release" DEP_CPP_JSHAS=\ ".\jsbit.h"\ ".\jscompat.h"\ ".\jscpucfg.h"\ ".\jshash.h"\ ".\jslong.h"\ ".\jsosdep.h"\ ".\jsotypes.h"\ ".\jstypes.h"\ ".\jsutil.h"\ {$(INCLUDE)}"\sys\types.h"\ NODEP_CPP_JSHAS=\ ".\jsautocfg.h"\ "$(INTDIR)\jshash.obj" : $(SOURCE) $(DEP_CPP_JSHAS) "$(INTDIR)" !ELSEIF "$(CFG)" == "js - Win32 Debug" DEP_CPP_JSHAS=\ ".\jsbit.h"\ ".\jscompat.h"\ ".\jscpucfg.h"\ ".\jshash.h"\ ".\jslong.h"\ ".\jsosdep.h"\ ".\jsotypes.h"\ ".\jstypes.h"\ ".\jsutil.h"\ {$(INCLUDE)}"\sys\types.h"\ NODEP_CPP_JSHAS=\ ".\jsautocfg.h"\ "$(INTDIR)\jshash.obj" : $(SOURCE) $(DEP_CPP_JSHAS) "$(INTDIR)" !ENDIF # End Source File ################################################################################ # Begin Source File SOURCE=.\jsinterp.c !IF "$(CFG)" == "js - Win32 Release" DEP_CPP_JSINT=\ ".\js.msg"\ ".\jsapi.h"\ ".\jsarena.h"\ ".\jsarray.h"\ ".\jsatom.h"\ ".\jsbool.h"\ ".\jsclist.h"\ ".\jscntxt.h"\ ".\jscompat.h"\ ".\jsconfig.h"\ ".\jscpucfg.h"\ ".\jsdbgapi.h"\ ".\jsfun.h"\ ".\jsgc.h"\ ".\jshash.h"\ ".\jsinterp.h"\ ".\jslock.h"\ ".\jslong.h"\ ".\jsnum.h"\ ".\jsobj.h"\ ".\jsopcode.h"\ ".\jsopcode.tbl"\ ".\jsosdep.h"\ ".\jsotypes.h"\ ".\jsprf.h"\ ".\jsprvtd.h"\ ".\jspubtd.h"\ ".\jsregexp.h"\ ".\jsscope.h"\ ".\jsscript.h"\ ".\jsstddef.h"\ ".\jsstr.h"\ ".\jstypes.h"\ ".\jsutil.h"\ ".\jsxml.h"\ {$(INCLUDE)}"\sys\types.h"\ NODEP_CPP_JSINT=\ ".\jsautocfg.h"\ ".\prcvar.h"\ ".\prlock.h"\ "$(INTDIR)\jsinterp.obj" : $(SOURCE) $(DEP_CPP_JSINT) "$(INTDIR)" !ELSEIF "$(CFG)" == "js - Win32 Debug" DEP_CPP_JSINT=\ ".\js.msg"\ ".\jsapi.h"\ ".\jsarena.h"\ ".\jsarray.h"\ ".\jsatom.h"\ ".\jsbool.h"\ ".\jsclist.h"\ ".\jscntxt.h"\ ".\jscompat.h"\ ".\jsconfig.h"\ ".\jscpucfg.h"\ ".\jsdbgapi.h"\ ".\jsfun.h"\ ".\jsgc.h"\ ".\jshash.h"\ ".\jsinterp.h"\ ".\jslock.h"\ ".\jslong.h"\ ".\jsnum.h"\ ".\jsobj.h"\ ".\jsopcode.h"\ ".\jsopcode.tbl"\ ".\jsosdep.h"\ ".\jsotypes.h"\ ".\jsprf.h"\ ".\jsprvtd.h"\ ".\jspubtd.h"\ ".\jsregexp.h"\ ".\jsscope.h"\ ".\jsscript.h"\ ".\jsstddef.h"\ ".\jsstr.h"\ ".\jstypes.h"\ ".\jsutil.h"\ ".\jsxml.h"\ {$(INCLUDE)}"\sys\types.h"\ NODEP_CPP_JSINT=\ ".\jsautocfg.h"\ ".\prcvar.h"\ ".\prlock.h"\ "$(INTDIR)\jsinterp.obj" : $(SOURCE) $(DEP_CPP_JSINT) "$(INTDIR)" !ENDIF # End Source File ################################################################################ # Begin Source File SOURCE=.\jslock.c !IF "$(CFG)" == "js - Win32 Release" DEP_CPP_JSLOC=\ ".\js.msg"\ ".\jsapi.h"\ ".\jsarena.h"\ ".\jsatom.h"\ ".\jsclist.h"\ ".\jscntxt.h"\ ".\jscompat.h"\ ".\jsconfig.h"\ ".\jscpucfg.h"\ ".\jsgc.h"\ ".\jshash.h"\ ".\jsinterp.h"\ ".\jslock.h"\ ".\jslong.h"\ ".\jsobj.h"\ ".\jsosdep.h"\ ".\jsotypes.h"\ ".\jsprvtd.h"\ ".\jspubtd.h"\ ".\jsregexp.h"\ ".\jsscope.h"\ ".\jsstddef.h"\ ".\jsstr.h"\ ".\jstypes.h"\ ".\jsutil.h"\ {$(INCLUDE)}"\sys\types.h"\ NODEP_CPP_JSLOC=\ ".\jsautocfg.h"\ ".\pratom.h"\ ".\prcvar.h"\ ".\prlock.h"\ ".\prthread.h"\ "$(INTDIR)\jslock.obj" : $(SOURCE) $(DEP_CPP_JSLOC) "$(INTDIR)" !ELSEIF "$(CFG)" == "js - Win32 Debug" DEP_CPP_JSLOC=\ ".\js.msg"\ ".\jsapi.h"\ ".\jsarena.h"\ ".\jsatom.h"\ ".\jsclist.h"\ ".\jscntxt.h"\ ".\jscompat.h"\ ".\jsconfig.h"\ ".\jscpucfg.h"\ ".\jsgc.h"\ ".\jshash.h"\ ".\jsinterp.h"\ ".\jslock.h"\ ".\jslong.h"\ ".\jsobj.h"\ ".\jsosdep.h"\ ".\jsotypes.h"\ ".\jsprvtd.h"\ ".\jspubtd.h"\ ".\jsregexp.h"\ ".\jsscope.h"\ ".\jsstddef.h"\ ".\jsstr.h"\ ".\jstypes.h"\ ".\jsutil.h"\ {$(INCLUDE)}"\sys\types.h"\ NODEP_CPP_JSLOC=\ ".\jsautocfg.h"\ ".\pratom.h"\ ".\prcvar.h"\ ".\prlock.h"\ ".\prthread.h"\ "$(INTDIR)\jslock.obj" : $(SOURCE) $(DEP_CPP_JSLOC) "$(INTDIR)" !ENDIF # End Source File ################################################################################ # Begin Source File SOURCE=.\jslog2.c !IF "$(CFG)" == "js - Win32 Release" DEP_CPP_JSLOG=\ ".\jsbit.h"\ ".\jscpucfg.h"\ ".\jsosdep.h"\ ".\jsotypes.h"\ ".\jstypes.h"\ {$(INCLUDE)}"\sys\types.h"\ NODEP_CPP_JSLOG=\ ".\jsautocfg.h"\ "$(INTDIR)\jslog2.obj" : $(SOURCE) $(DEP_CPP_JSLOG) "$(INTDIR)" !ELSEIF "$(CFG)" == "js - Win32 Debug" DEP_CPP_JSLOG=\ ".\jsbit.h"\ ".\jscpucfg.h"\ ".\jsosdep.h"\ ".\jsotypes.h"\ ".\jstypes.h"\ {$(INCLUDE)}"\sys\types.h"\ NODEP_CPP_JSLOG=\ ".\jsautocfg.h"\ "$(INTDIR)\jslog2.obj" : $(SOURCE) $(DEP_CPP_JSLOG) "$(INTDIR)" !ENDIF # End Source File ################################################################################ # Begin Source File SOURCE=.\jslong.c !IF "$(CFG)" == "js - Win32 Release" DEP_CPP_JSLON=\ ".\jscpucfg.h"\ ".\jslong.h"\ ".\jsosdep.h"\ ".\jsotypes.h"\ ".\jstypes.h"\ {$(INCLUDE)}"\sys\types.h"\ NODEP_CPP_JSLON=\ ".\jsautocfg.h"\ "$(INTDIR)\jslong.obj" : $(SOURCE) $(DEP_CPP_JSLON) "$(INTDIR)" !ELSEIF "$(CFG)" == "js - Win32 Debug" DEP_CPP_JSLON=\ ".\jscpucfg.h"\ ".\jslong.h"\ ".\jsosdep.h"\ ".\jsotypes.h"\ ".\jstypes.h"\ {$(INCLUDE)}"\sys\types.h"\ NODEP_CPP_JSLON=\ ".\jsautocfg.h"\ "$(INTDIR)\jslong.obj" : $(SOURCE) $(DEP_CPP_JSLON) "$(INTDIR)" !ENDIF # End Source File ################################################################################ # Begin Source File SOURCE=.\jsmath.c !IF "$(CFG)" == "js - Win32 Release" DEP_CPP_JSMAT=\ ".\js.msg"\ ".\jsapi.h"\ ".\jsarena.h"\ ".\jsatom.h"\ ".\jsclist.h"\ ".\jscntxt.h"\ ".\jscompat.h"\ ".\jsconfig.h"\ ".\jscpucfg.h"\ ".\jsgc.h"\ ".\jshash.h"\ ".\jsinterp.h"\ ".\jslibmath.h"\ ".\jslock.h"\ ".\jslong.h"\ ".\jsmath.h"\ ".\jsnum.h"\ ".\jsobj.h"\ ".\jsosdep.h"\ ".\jsotypes.h"\ ".\jsprvtd.h"\ ".\jspubtd.h"\ ".\jsregexp.h"\ ".\jsscope.h"\ ".\jsstddef.h"\ ".\jsstr.h"\ ".\jstypes.h"\ ".\prmjtime.h"\ {$(INCLUDE)}"\sys\types.h"\ NODEP_CPP_JSMAT=\ ".\jsautocfg.h"\ ".\prcvar.h"\ ".\prlock.h"\ "$(INTDIR)\jsmath.obj" : $(SOURCE) $(DEP_CPP_JSMAT) "$(INTDIR)" !ELSEIF "$(CFG)" == "js - Win32 Debug" DEP_CPP_JSMAT=\ ".\js.msg"\ ".\jsapi.h"\ ".\jsarena.h"\ ".\jsatom.h"\ ".\jsclist.h"\ ".\jscntxt.h"\ ".\jscompat.h"\ ".\jsconfig.h"\ ".\jscpucfg.h"\ ".\jsgc.h"\ ".\jshash.h"\ ".\jsinterp.h"\ ".\jslibmath.h"\ ".\jslock.h"\ ".\jslong.h"\ ".\jsmath.h"\ ".\jsnum.h"\ ".\jsobj.h"\ ".\jsosdep.h"\ ".\jsotypes.h"\ ".\jsprvtd.h"\ ".\jspubtd.h"\ ".\jsregexp.h"\ ".\jsscope.h"\ ".\jsstddef.h"\ ".\jsstr.h"\ ".\jstypes.h"\ ".\prmjtime.h"\ {$(INCLUDE)}"\sys\types.h"\ NODEP_CPP_JSMAT=\ ".\jsautocfg.h"\ ".\prcvar.h"\ ".\prlock.h"\ "$(INTDIR)\jsmath.obj" : $(SOURCE) $(DEP_CPP_JSMAT) "$(INTDIR)" !ENDIF # End Source File ################################################################################ # Begin Source File SOURCE=.\jsnum.c !IF "$(CFG)" == "js - Win32 Release" DEP_CPP_JSNUM=\ ".\js.msg"\ ".\jsapi.h"\ ".\jsarena.h"\ ".\jsatom.h"\ ".\jsclist.h"\ ".\jscntxt.h"\ ".\jscompat.h"\ ".\jsconfig.h"\ ".\jscpucfg.h"\ ".\jsdtoa.h"\ ".\jsgc.h"\ ".\jshash.h"\ ".\jsinterp.h"\ ".\jslock.h"\ ".\jslong.h"\ ".\jsnum.h"\ ".\jsobj.h"\ ".\jsopcode.h"\ ".\jsopcode.tbl"\ ".\jsosdep.h"\ ".\jsotypes.h"\ ".\jsprf.h"\ ".\jsprvtd.h"\ ".\jspubtd.h"\ ".\jsregexp.h"\ ".\jsscope.h"\ ".\jsstddef.h"\ ".\jsstr.h"\ ".\jstypes.h"\ ".\jsutil.h"\ {$(INCLUDE)}"\sys\types.h"\ NODEP_CPP_JSNUM=\ ".\jsautocfg.h"\ ".\prcvar.h"\ ".\prlock.h"\ "$(INTDIR)\jsnum.obj" : $(SOURCE) $(DEP_CPP_JSNUM) "$(INTDIR)" !ELSEIF "$(CFG)" == "js - Win32 Debug" DEP_CPP_JSNUM=\ ".\js.msg"\ ".\jsapi.h"\ ".\jsarena.h"\ ".\jsatom.h"\ ".\jsclist.h"\ ".\jscntxt.h"\ ".\jscompat.h"\ ".\jsconfig.h"\ ".\jscpucfg.h"\ ".\jsdtoa.h"\ ".\jsgc.h"\ ".\jshash.h"\ ".\jsinterp.h"\ ".\jslock.h"\ ".\jslong.h"\ ".\jsnum.h"\ ".\jsobj.h"\ ".\jsopcode.h"\ ".\jsopcode.tbl"\ ".\jsosdep.h"\ ".\jsotypes.h"\ ".\jsprf.h"\ ".\jsprvtd.h"\ ".\jspubtd.h"\ ".\jsregexp.h"\ ".\jsscope.h"\ ".\jsstddef.h"\ ".\jsstr.h"\ ".\jstypes.h"\ ".\jsutil.h"\ {$(INCLUDE)}"\sys\types.h"\ NODEP_CPP_JSNUM=\ ".\jsautocfg.h"\ ".\prcvar.h"\ ".\prlock.h"\ "$(INTDIR)\jsnum.obj" : $(SOURCE) $(DEP_CPP_JSNUM) "$(INTDIR)" !ENDIF # End Source File ################################################################################ # Begin Source File SOURCE=.\jsobj.c !IF "$(CFG)" == "js - Win32 Release" DEP_CPP_JSOBJ=\ ".\js.msg"\ ".\jsapi.h"\ ".\jsarena.h"\ ".\jsatom.h"\ ".\jsbool.h"\ ".\jsclist.h"\ ".\jscntxt.h"\ ".\jscompat.h"\ ".\jsconfig.h"\ ".\jscpucfg.h"\ ".\jsdbgapi.h"\ ".\jsfun.h"\ ".\jsgc.h"\ ".\jshash.h"\ ".\jsinterp.h"\ ".\jslock.h"\ ".\jslong.h"\ ".\jsnum.h"\ ".\jsobj.h"\ ".\jsopcode.h"\ ".\jsopcode.tbl"\ ".\jsosdep.h"\ ".\jsotypes.h"\ ".\jsprf.h"\ ".\jsprvtd.h"\ ".\jspubtd.h"\ ".\jsregexp.h"\ ".\jsscope.h"\ ".\jsscript.h"\ ".\jsstddef.h"\ ".\jsstr.h"\ ".\jstypes.h"\ ".\jsutil.h"\ ".\jsxdrapi.h"\ {$(INCLUDE)}"\sys\types.h"\ NODEP_CPP_JSOBJ=\ ".\jsautocfg.h"\ ".\prcvar.h"\ ".\prlock.h"\ "$(INTDIR)\jsobj.obj" : $(SOURCE) $(DEP_CPP_JSOBJ) "$(INTDIR)" !ELSEIF "$(CFG)" == "js - Win32 Debug" DEP_CPP_JSOBJ=\ ".\js.msg"\ ".\jsapi.h"\ ".\jsarena.h"\ ".\jsatom.h"\ ".\jsbool.h"\ ".\jsclist.h"\ ".\jscntxt.h"\ ".\jscompat.h"\ ".\jsconfig.h"\ ".\jscpucfg.h"\ ".\jsdbgapi.h"\ ".\jsfun.h"\ ".\jsgc.h"\ ".\jshash.h"\ ".\jsinterp.h"\ ".\jslock.h"\ ".\jslong.h"\ ".\jsnum.h"\ ".\jsobj.h"\ ".\jsopcode.h"\ ".\jsopcode.tbl"\ ".\jsosdep.h"\ ".\jsotypes.h"\ ".\jsprf.h"\ ".\jsprvtd.h"\ ".\jspubtd.h"\ ".\jsregexp.h"\ ".\jsscope.h"\ ".\jsscript.h"\ ".\jsstddef.h"\ ".\jsstr.h"\ ".\jstypes.h"\ ".\jsutil.h"\ ".\jsxdrapi.h"\ {$(INCLUDE)}"\sys\types.h"\ NODEP_CPP_JSOBJ=\ ".\jsautocfg.h"\ ".\prcvar.h"\ ".\prlock.h"\ "$(INTDIR)\jsobj.obj" : $(SOURCE) $(DEP_CPP_JSOBJ) "$(INTDIR)" !ENDIF # End Source File ################################################################################ # Begin Source File SOURCE=.\jsopcode.c !IF "$(CFG)" == "js - Win32 Release" DEP_CPP_JSOPC=\ ".\js.msg"\ ".\jsapi.h"\ ".\jsarena.h"\ ".\jsarray.h"\ ".\jsatom.h"\ ".\jsclist.h"\ ".\jscntxt.h"\ ".\jscompat.h"\ ".\jsconfig.h"\ ".\jscpucfg.h"\ ".\jsdbgapi.h"\ ".\jsdtoa.h"\ ".\jsemit.h"\ ".\jsfun.h"\ ".\jsgc.h"\ ".\jshash.h"\ ".\jsinterp.h"\ ".\jslock.h"\ ".\jslong.h"\ ".\jsobj.h"\ ".\jsopcode.h"\ ".\jsopcode.tbl"\ ".\jsosdep.h"\ ".\jsotypes.h"\ ".\jsprf.h"\ ".\jsprvtd.h"\ ".\jspubtd.h"\ ".\jsregexp.h"\ ".\jsscope.h"\ ".\jsscript.h"\ ".\jsstddef.h"\ ".\jsstr.h"\ ".\jstypes.h"\ ".\jsutil.h"\ {$(INCLUDE)}"\sys\types.h"\ NODEP_CPP_JSOPC=\ ".\jsautocfg.h"\ ".\prcvar.h"\ ".\prlock.h"\ "$(INTDIR)\jsopcode.obj" : $(SOURCE) $(DEP_CPP_JSOPC) "$(INTDIR)" !ELSEIF "$(CFG)" == "js - Win32 Debug" DEP_CPP_JSOPC=\ ".\js.msg"\ ".\jsapi.h"\ ".\jsarena.h"\ ".\jsarray.h"\ ".\jsatom.h"\ ".\jsclist.h"\ ".\jscntxt.h"\ ".\jscompat.h"\ ".\jsconfig.h"\ ".\jscpucfg.h"\ ".\jsdbgapi.h"\ ".\jsdtoa.h"\ ".\jsemit.h"\ ".\jsfun.h"\ ".\jsgc.h"\ ".\jshash.h"\ ".\jsinterp.h"\ ".\jslock.h"\ ".\jslong.h"\ ".\jsobj.h"\ ".\jsopcode.h"\ ".\jsopcode.tbl"\ ".\jsosdep.h"\ ".\jsotypes.h"\ ".\jsprf.h"\ ".\jsprvtd.h"\ ".\jspubtd.h"\ ".\jsregexp.h"\ ".\jsscope.h"\ ".\jsscript.h"\ ".\jsstddef.h"\ ".\jsstr.h"\ ".\jstypes.h"\ ".\jsutil.h"\ {$(INCLUDE)}"\sys\types.h"\ NODEP_CPP_JSOPC=\ ".\jsautocfg.h"\ ".\prcvar.h"\ ".\prlock.h"\ "$(INTDIR)\jsopcode.obj" : $(SOURCE) $(DEP_CPP_JSOPC) "$(INTDIR)" !ENDIF # End Source File ################################################################################ # Begin Source File SOURCE=.\jsparse.c !IF "$(CFG)" == "js - Win32 Release" DEP_CPP_JSPAR=\ ".\js.msg"\ ".\jsapi.h"\ ".\jsarena.h"\ ".\jsatom.h"\ ".\jsclist.h"\ ".\jscntxt.h"\ ".\jscompat.h"\ ".\jsconfig.h"\ ".\jscpucfg.h"\ ".\jsemit.h"\ ".\jsfun.h"\ ".\jsgc.h"\ ".\jshash.h"\ ".\jsinterp.h"\ ".\jslock.h"\ ".\jslong.h"\ ".\jsnum.h"\ ".\jsobj.h"\ ".\jsopcode.h"\ ".\jsopcode.tbl"\ ".\jsosdep.h"\ ".\jsotypes.h"\ ".\jsparse.h"\ ".\jsprvtd.h"\ ".\jspubtd.h"\ ".\jsregexp.h"\ ".\jsscan.h"\ ".\jsscope.h"\ ".\jsscript.h"\ ".\jsstddef.h"\ ".\jsstr.h"\ ".\jstypes.h"\ ".\jsutil.h"\ {$(INCLUDE)}"\sys\types.h"\ NODEP_CPP_JSPAR=\ ".\jsautocfg.h"\ ".\prcvar.h"\ ".\prlock.h"\ "$(INTDIR)\jsparse.obj" : $(SOURCE) $(DEP_CPP_JSPAR) "$(INTDIR)" !ELSEIF "$(CFG)" == "js - Win32 Debug" DEP_CPP_JSPAR=\ ".\js.msg"\ ".\jsapi.h"\ ".\jsarena.h"\ ".\jsatom.h"\ ".\jsclist.h"\ ".\jscntxt.h"\ ".\jscompat.h"\ ".\jsconfig.h"\ ".\jscpucfg.h"\ ".\jsemit.h"\ ".\jsfun.h"\ ".\jsgc.h"\ ".\jshash.h"\ ".\jsinterp.h"\ ".\jslock.h"\ ".\jslong.h"\ ".\jsnum.h"\ ".\jsobj.h"\ ".\jsopcode.h"\ ".\jsopcode.tbl"\ ".\jsosdep.h"\ ".\jsotypes.h"\ ".\jsparse.h"\ ".\jsprvtd.h"\ ".\jspubtd.h"\ ".\jsregexp.h"\ ".\jsscan.h"\ ".\jsscope.h"\ ".\jsscript.h"\ ".\jsstddef.h"\ ".\jsstr.h"\ ".\jstypes.h"\ ".\jsutil.h"\ {$(INCLUDE)}"\sys\types.h"\ NODEP_CPP_JSPAR=\ ".\jsautocfg.h"\ ".\prcvar.h"\ ".\prlock.h"\ "$(INTDIR)\jsparse.obj" : $(SOURCE) $(DEP_CPP_JSPAR) "$(INTDIR)" !ENDIF # End Source File ################################################################################ # Begin Source File SOURCE=.\jsprf.c !IF "$(CFG)" == "js - Win32 Release" DEP_CPP_JSPRF=\ ".\jscpucfg.h"\ ".\jslong.h"\ ".\jsosdep.h"\ ".\jsotypes.h"\ ".\jsprf.h"\ ".\jstypes.h"\ ".\jsutil.h"\ {$(INCLUDE)}"\sys\types.h"\ NODEP_CPP_JSPRF=\ ".\jsautocfg.h"\ "$(INTDIR)\jsprf.obj" : $(SOURCE) $(DEP_CPP_JSPRF) "$(INTDIR)" !ELSEIF "$(CFG)" == "js - Win32 Debug" DEP_CPP_JSPRF=\ ".\jscpucfg.h"\ ".\jslong.h"\ ".\jsosdep.h"\ ".\jsotypes.h"\ ".\jsprf.h"\ ".\jstypes.h"\ ".\jsutil.h"\ {$(INCLUDE)}"\sys\types.h"\ NODEP_CPP_JSPRF=\ ".\jsautocfg.h"\ "$(INTDIR)\jsprf.obj" : $(SOURCE) $(DEP_CPP_JSPRF) "$(INTDIR)" !ENDIF # End Source File ################################################################################ # Begin Source File SOURCE=.\jsregexp.c !IF "$(CFG)" == "js - Win32 Release" DEP_CPP_JSREG=\ ".\js.msg"\ ".\jsapi.h"\ ".\jsarena.h"\ ".\jsarray.h"\ ".\jsatom.h"\ ".\jsclist.h"\ ".\jscntxt.h"\ ".\jscompat.h"\ ".\jsconfig.h"\ ".\jscpucfg.h"\ ".\jsfun.h"\ ".\jsgc.h"\ ".\jshash.h"\ ".\jsinterp.h"\ ".\jslock.h"\ ".\jslong.h"\ ".\jsnum.h"\ ".\jsobj.h"\ ".\jsopcode.h"\ ".\jsopcode.tbl"\ ".\jsosdep.h"\ ".\jsotypes.h"\ ".\jsprvtd.h"\ ".\jspubtd.h"\ ".\jsregexp.h"\ ".\jsscope.h"\ ".\jsstddef.h"\ ".\jsstr.h"\ ".\jstypes.h"\ ".\jsutil.h"\ ".\jsxdrapi.h"\ {$(INCLUDE)}"\sys\types.h"\ NODEP_CPP_JSREG=\ ".\jsautocfg.h"\ ".\prcvar.h"\ ".\prlock.h"\ "$(INTDIR)\jsregexp.obj" : $(SOURCE) $(DEP_CPP_JSREG) "$(INTDIR)" !ELSEIF "$(CFG)" == "js - Win32 Debug" DEP_CPP_JSREG=\ ".\js.msg"\ ".\jsapi.h"\ ".\jsarena.h"\ ".\jsarray.h"\ ".\jsatom.h"\ ".\jsclist.h"\ ".\jscntxt.h"\ ".\jscompat.h"\ ".\jsconfig.h"\ ".\jscpucfg.h"\ ".\jsfun.h"\ ".\jsgc.h"\ ".\jshash.h"\ ".\jsinterp.h"\ ".\jslock.h"\ ".\jslong.h"\ ".\jsnum.h"\ ".\jsobj.h"\ ".\jsopcode.h"\ ".\jsopcode.tbl"\ ".\jsosdep.h"\ ".\jsotypes.h"\ ".\jsprvtd.h"\ ".\jspubtd.h"\ ".\jsregexp.h"\ ".\jsscope.h"\ ".\jsstddef.h"\ ".\jsstr.h"\ ".\jstypes.h"\ ".\jsutil.h"\ ".\jsxdrapi.h"\ {$(INCLUDE)}"\sys\types.h"\ NODEP_CPP_JSREG=\ ".\jsautocfg.h"\ ".\prcvar.h"\ ".\prlock.h"\ "$(INTDIR)\jsregexp.obj" : $(SOURCE) $(DEP_CPP_JSREG) "$(INTDIR)" !ENDIF # End Source File ################################################################################ # Begin Source File SOURCE=.\jsscan.c !IF "$(CFG)" == "js - Win32 Release" DEP_CPP_JSSCA=\ ".\js.msg"\ ".\jsapi.h"\ ".\jsarena.h"\ ".\jsatom.h"\ ".\jsclist.h"\ ".\jscntxt.h"\ ".\jscompat.h"\ ".\jsconfig.h"\ ".\jscpucfg.h"\ ".\jsdtoa.h"\ ".\jsexn.h"\ ".\jsgc.h"\ ".\jshash.h"\ ".\jsinterp.h"\ ".\jslock.h"\ ".\jslong.h"\ ".\jsnum.h"\ ".\jsobj.h"\ ".\jsopcode.h"\ ".\jsopcode.tbl"\ ".\jsosdep.h"\ ".\jsotypes.h"\ ".\jsprf.h"\ ".\jsprvtd.h"\ ".\jspubtd.h"\ ".\jsregexp.h"\ ".\jsscan.h"\ ".\jsscope.h"\ ".\jsstddef.h"\ ".\jsstr.h"\ ".\jstypes.h"\ ".\jsutil.h"\ ".\jsxml.h"\ {$(INCLUDE)}"\sys\types.h"\ NODEP_CPP_JSSCA=\ ".\jsautocfg.h"\ ".\prcvar.h"\ ".\prlock.h"\ "$(INTDIR)\jsscan.obj" : $(SOURCE) $(DEP_CPP_JSSCA) "$(INTDIR)" "$(INTDIR)\jsautokw.h" "$(INTDIR)\jsautokw.h" : $(INTDIR)\host_jskwgen.exe jskeyword.tbl $(INTDIR)\host_jskwgen.exe $(INTDIR)\jsautokw.h !ELSEIF "$(CFG)" == "js - Win32 Debug" DEP_CPP_JSSCA=\ ".\js.msg"\ ".\jsapi.h"\ ".\jsarena.h"\ ".\jsatom.h"\ ".\jsclist.h"\ ".\jscntxt.h"\ ".\jscompat.h"\ ".\jsconfig.h"\ ".\jscpucfg.h"\ ".\jsdtoa.h"\ ".\jsexn.h"\ ".\jsgc.h"\ ".\jshash.h"\ ".\jsinterp.h"\ ".\jslock.h"\ ".\jslong.h"\ ".\jsnum.h"\ ".\jsobj.h"\ ".\jsopcode.h"\ ".\jsopcode.tbl"\ ".\jsosdep.h"\ ".\jsotypes.h"\ ".\jsprf.h"\ ".\jsprvtd.h"\ ".\jspubtd.h"\ ".\jsregexp.h"\ ".\jsscan.h"\ ".\jsscope.h"\ ".\jsstddef.h"\ ".\jsstr.h"\ ".\jstypes.h"\ ".\jsutil.h"\ ".\jsxml.h"\ {$(INCLUDE)}"\sys\types.h"\ $(INTDIR)\jsautokw.h \ NODEP_CPP_JSSCA=\ ".\jsautocfg.h"\ ".\prcvar.h"\ ".\prlock.h"\ "$(INTDIR)\jsscan.obj" : $(SOURCE) $(DEP_CPP_JSSCA) "$(INTDIR)" "$(INTDIR)\jsautokw.h" : $(INTDIR)\host_jskwgen.exe jskeyword.tbl $(INTDIR)\host_jskwgen.exe $(INTDIR)\jsautokw.h !ENDIF # End Source File ################################################################################ # Begin Source File SOURCE=.\jskwgen.c !IF "$(CFG)" == "js - Win32 Release" DEP_CPP_JSSCO=\ ".\jskwgen.c"\ {$(INCLUDE)}"\sys\types.h"\ "$(INTDIR)\jskwgen.obj" : $(SOURCE) $(DEP_CPP_JSSCO) "$(INTDIR)" !ELSEIF "$(CFG)" == "js - Win32 Debug" DEP_CPP_JSSCO=\ ".\jskwgen.c"\ {$(INCLUDE)}"\sys\types.h"\ LINK32_FLAGS=kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib\ advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib\ odbccp32.lib /nologo /subsystem:console /incremental:no\ /pdb:"$(INTDIR)/host_jskwgen.pdb" /machine:I386 /out:"$(INTDIR)/host_jskwgen.exe" LINK32_OBJS= \ "$(INTDIR)\jskwgen.obj" "$(INTDIR)\host_jskwgen.exe" : "$(INTDIR)" $(SOURCE) $(DEP_CPP_JSSCO) "$(INTDIR)" $(LINK32) @<< $(LINK32_FLAGS) $(LINK32_OBJS) << !ENDIF # End Source File ################################################################################ # Begin Source File SOURCE=.\jsscope.c !IF "$(CFG)" == "js - Win32 Release" DEP_CPP_JSSCO=\ ".\js.msg"\ ".\jsapi.h"\ ".\jsarena.h"\ ".\jsatom.h"\ ".\jsclist.h"\ ".\jscntxt.h"\ ".\jscompat.h"\ ".\jsconfig.h"\ ".\jscpucfg.h"\ ".\jsgc.h"\ ".\jshash.h"\ ".\jsinterp.h"\ ".\jslock.h"\ ".\jslong.h"\ ".\jsnum.h"\ ".\jsobj.h"\ ".\jsosdep.h"\ ".\jsotypes.h"\ ".\jsprvtd.h"\ ".\jspubtd.h"\ ".\jsregexp.h"\ ".\jsscope.h"\ ".\jsstddef.h"\ ".\jsstr.h"\ ".\jstypes.h"\ ".\jsutil.h"\ {$(INCLUDE)}"\sys\types.h"\ NODEP_CPP_JSSCO=\ ".\jsautocfg.h"\ ".\prcvar.h"\ ".\prlock.h"\ "$(INTDIR)\jsscope.obj" : $(SOURCE) $(DEP_CPP_JSSCO) "$(INTDIR)" !ELSEIF "$(CFG)" == "js - Win32 Debug" DEP_CPP_JSSCO=\ ".\js.msg"\ ".\jsapi.h"\ ".\jsarena.h"\ ".\jsatom.h"\ ".\jsclist.h"\ ".\jscntxt.h"\ ".\jscompat.h"\ ".\jsconfig.h"\ ".\jscpucfg.h"\ ".\jsgc.h"\ ".\jshash.h"\ ".\jsinterp.h"\ ".\jslock.h"\ ".\jslong.h"\ ".\jsnum.h"\ ".\jsobj.h"\ ".\jsosdep.h"\ ".\jsotypes.h"\ ".\jsprvtd.h"\ ".\jspubtd.h"\ ".\jsregexp.h"\ ".\jsscope.h"\ ".\jsstddef.h"\ ".\jsstr.h"\ ".\jstypes.h"\ ".\jsutil.h"\ {$(INCLUDE)}"\sys\types.h"\ NODEP_CPP_JSSCO=\ ".\jsautocfg.h"\ ".\prcvar.h"\ ".\prlock.h"\ "$(INTDIR)\jsscope.obj" : $(SOURCE) $(DEP_CPP_JSSCO) "$(INTDIR)" !ENDIF # End Source File ################################################################################ # Begin Source File SOURCE=.\jsscript.c !IF "$(CFG)" == "js - Win32 Release" DEP_CPP_JSSCR=\ ".\js.msg"\ ".\jsapi.h"\ ".\jsarena.h"\ ".\jsatom.h"\ ".\jsclist.h"\ ".\jscntxt.h"\ ".\jscompat.h"\ ".\jsconfig.h"\ ".\jscpucfg.h"\ ".\jsdbgapi.h"\ ".\jsemit.h"\ ".\jsfun.h"\ ".\jsgc.h"\ ".\jshash.h"\ ".\jsinterp.h"\ ".\jslock.h"\ ".\jslong.h"\ ".\jsnum.h"\ ".\jsobj.h"\ ".\jsopcode.h"\ ".\jsopcode.tbl"\ ".\jsosdep.h"\ ".\jsotypes.h"\ ".\jsprf.h"\ ".\jsprvtd.h"\ ".\jspubtd.h"\ ".\jsregexp.h"\ ".\jsscope.h"\ ".\jsscript.h"\ ".\jsstddef.h"\ ".\jsstr.h"\ ".\jstypes.h"\ ".\jsutil.h"\ ".\jsxdrapi.h"\ {$(INCLUDE)}"\sys\types.h"\ NODEP_CPP_JSSCR=\ ".\jsautocfg.h"\ ".\prcvar.h"\ ".\prlock.h"\ "$(INTDIR)\jsscript.obj" : $(SOURCE) $(DEP_CPP_JSSCR) "$(INTDIR)" !ELSEIF "$(CFG)" == "js - Win32 Debug" DEP_CPP_JSSCR=\ ".\js.msg"\ ".\jsapi.h"\ ".\jsarena.h"\ ".\jsatom.h"\ ".\jsclist.h"\ ".\jscntxt.h"\ ".\jscompat.h"\ ".\jsconfig.h"\ ".\jscpucfg.h"\ ".\jsdbgapi.h"\ ".\jsemit.h"\ ".\jsfun.h"\ ".\jsgc.h"\ ".\jshash.h"\ ".\jsinterp.h"\ ".\jslock.h"\ ".\jslong.h"\ ".\jsnum.h"\ ".\jsobj.h"\ ".\jsopcode.h"\ ".\jsopcode.tbl"\ ".\jsosdep.h"\ ".\jsotypes.h"\ ".\jsprf.h"\ ".\jsprvtd.h"\ ".\jspubtd.h"\ ".\jsregexp.h"\ ".\jsscope.h"\ ".\jsscript.h"\ ".\jsstddef.h"\ ".\jsstr.h"\ ".\jstypes.h"\ ".\jsutil.h"\ ".\jsxdrapi.h"\ {$(INCLUDE)}"\sys\types.h"\ NODEP_CPP_JSSCR=\ ".\jsautocfg.h"\ ".\prcvar.h"\ ".\prlock.h"\ "$(INTDIR)\jsscript.obj" : $(SOURCE) $(DEP_CPP_JSSCR) "$(INTDIR)" !ENDIF # End Source File ################################################################################ # Begin Source File SOURCE=.\jsstr.c !IF "$(CFG)" == "js - Win32 Release" DEP_CPP_JSSTR=\ ".\js.msg"\ ".\jsapi.h"\ ".\jsarena.h"\ ".\jsarray.h"\ ".\jsatom.h"\ ".\jsbool.h"\ ".\jsclist.h"\ ".\jscntxt.h"\ ".\jscompat.h"\ ".\jsconfig.h"\ ".\jscpucfg.h"\ ".\jsgc.h"\ ".\jshash.h"\ ".\jsinterp.h"\ ".\jslock.h"\ ".\jslong.h"\ ".\jsnum.h"\ ".\jsobj.h"\ ".\jsopcode.h"\ ".\jsopcode.tbl"\ ".\jsosdep.h"\ ".\jsotypes.h"\ ".\jsprf.h"\ ".\jsprvtd.h"\ ".\jspubtd.h"\ ".\jsregexp.h"\ ".\jsscope.h"\ ".\jsstddef.h"\ ".\jsstr.h"\ ".\jstypes.h"\ ".\jsutil.h"\ {$(INCLUDE)}"\sys\types.h"\ NODEP_CPP_JSSTR=\ ".\jsautocfg.h"\ ".\prcvar.h"\ ".\prlock.h"\ "$(INTDIR)\jsstr.obj" : $(SOURCE) $(DEP_CPP_JSSTR) "$(INTDIR)" !ELSEIF "$(CFG)" == "js - Win32 Debug" DEP_CPP_JSSTR=\ ".\js.msg"\ ".\jsapi.h"\ ".\jsarena.h"\ ".\jsarray.h"\ ".\jsatom.h"\ ".\jsbool.h"\ ".\jsclist.h"\ ".\jscntxt.h"\ ".\jscompat.h"\ ".\jsconfig.h"\ ".\jscpucfg.h"\ ".\jsgc.h"\ ".\jshash.h"\ ".\jsinterp.h"\ ".\jslock.h"\ ".\jslong.h"\ ".\jsnum.h"\ ".\jsobj.h"\ ".\jsopcode.h"\ ".\jsopcode.tbl"\ ".\jsosdep.h"\ ".\jsotypes.h"\ ".\jsprf.h"\ ".\jsprvtd.h"\ ".\jspubtd.h"\ ".\jsregexp.h"\ ".\jsscope.h"\ ".\jsstddef.h"\ ".\jsstr.h"\ ".\jstypes.h"\ ".\jsutil.h"\ {$(INCLUDE)}"\sys\types.h"\ NODEP_CPP_JSSTR=\ ".\jsautocfg.h"\ ".\prcvar.h"\ ".\prlock.h"\ "$(INTDIR)\jsstr.obj" : $(SOURCE) $(DEP_CPP_JSSTR) "$(INTDIR)" !ENDIF # End Source File ################################################################################ # Begin Source File SOURCE=.\jsutil.c !IF "$(CFG)" == "js - Win32 Release" DEP_CPP_JSUTI=\ ".\jscpucfg.h"\ ".\jsosdep.h"\ ".\jsotypes.h"\ ".\jsprf.h"\ ".\jstypes.h"\ ".\jsutil.h"\ {$(INCLUDE)}"\sys\types.h"\ NODEP_CPP_JSUTI=\ ".\jsautocfg.h"\ "$(INTDIR)\jsutil.obj" : $(SOURCE) $(DEP_CPP_JSUTI) "$(INTDIR)" !ELSEIF "$(CFG)" == "js - Win32 Debug" DEP_CPP_JSUTI=\ ".\jscpucfg.h"\ ".\jsosdep.h"\ ".\jsotypes.h"\ ".\jsprf.h"\ ".\jstypes.h"\ ".\jsutil.h"\ {$(INCLUDE)}"\sys\types.h"\ NODEP_CPP_JSUTI=\ ".\jsautocfg.h"\ "$(INTDIR)\jsutil.obj" : $(SOURCE) $(DEP_CPP_JSUTI) "$(INTDIR)" !ENDIF # End Source File ################################################################################ # Begin Source File SOURCE=.\jsxdrapi.c !IF "$(CFG)" == "js - Win32 Release" DEP_CPP_JSXDR=\ ".\js.msg"\ ".\jsapi.h"\ ".\jsarena.h"\ ".\jsatom.h"\ ".\jsclist.h"\ ".\jscntxt.h"\ ".\jscompat.h"\ ".\jsconfig.h"\ ".\jscpucfg.h"\ ".\jsgc.h"\ ".\jshash.h"\ ".\jsinterp.h"\ ".\jslock.h"\ ".\jslong.h"\ ".\jsobj.h"\ ".\jsosdep.h"\ ".\jsotypes.h"\ ".\jsprf.h"\ ".\jsprvtd.h"\ ".\jspubtd.h"\ ".\jsregexp.h"\ ".\jsscope.h"\ ".\jsstddef.h"\ ".\jsstr.h"\ ".\jstypes.h"\ ".\jsutil.h"\ ".\jsxdrapi.h"\ {$(INCLUDE)}"\sys\types.h"\ NODEP_CPP_JSXDR=\ ".\jsautocfg.h"\ ".\prcvar.h"\ ".\prlock.h"\ "$(INTDIR)\jsxdrapi.obj" : $(SOURCE) $(DEP_CPP_JSXDR) "$(INTDIR)" !ELSEIF "$(CFG)" == "js - Win32 Debug" DEP_CPP_JSXDR=\ ".\js.msg"\ ".\jsapi.h"\ ".\jsarena.h"\ ".\jsatom.h"\ ".\jsclist.h"\ ".\jscntxt.h"\ ".\jscompat.h"\ ".\jsconfig.h"\ ".\jscpucfg.h"\ ".\jsgc.h"\ ".\jshash.h"\ ".\jsinterp.h"\ ".\jslock.h"\ ".\jslong.h"\ ".\jsobj.h"\ ".\jsosdep.h"\ ".\jsotypes.h"\ ".\jsprf.h"\ ".\jsprvtd.h"\ ".\jspubtd.h"\ ".\jsregexp.h"\ ".\jsscope.h"\ ".\jsstddef.h"\ ".\jsstr.h"\ ".\jstypes.h"\ ".\jsutil.h"\ ".\jsxdrapi.h"\ {$(INCLUDE)}"\sys\types.h"\ NODEP_CPP_JSXDR=\ ".\jsautocfg.h"\ ".\prcvar.h"\ ".\prlock.h"\ "$(INTDIR)\jsxdrapi.obj" : $(SOURCE) $(DEP_CPP_JSXDR) "$(INTDIR)" !ENDIF # End Source File ################################################################################ # Begin Source File SOURCE=.\jsxml.c !IF "$(CFG)" == "js - Win32 Release" DEP_CPP_JSXML=\ ".\js.msg"\ ".\jsapi.h"\ ".\jsarray.h"\ ".\jsatom.h"\ ".\jsbit.h"\ ".\jsbool.h"\ ".\jscntxt.h"\ ".\jsfun.h"\ ".\jsgc.h"\ ".\jsinterp.h"\ ".\jslock.h"\ ".\jsnum.h"\ ".\jsobj.h"\ ".\jsopcode.h"\ ".\jsparse.h"\ ".\jsprf.h"\ ".\jsscan.h"\ ".\jsscope.h"\ ".\jsscript.h"\ ".\jsstr.h"\ ".\jstypes.h"\ ".\jsutil.h"\ ".\jsxml.h"\ {$(INCLUDE)}"\sys\types.h"\ NODEP_CPP_JSXML=\ ".\jsautocfg.h"\ ".\prcvar.h"\ ".\prlock.h"\ "$(INTDIR)\jsxml.obj" : $(SOURCE) $(DEP_CPP_JSXML) "$(INTDIR)" !ELSEIF "$(CFG)" == "js - Win32 Debug" DEP_CPP_JSXML=\ ".\js.msg"\ ".\jsapi.h"\ ".\jsarray.h"\ ".\jsatom.h"\ ".\jsbit.h"\ ".\jsbool.h"\ ".\jscntxt.h"\ ".\jsfun.h"\ ".\jsgc.h"\ ".\jsinterp.h"\ ".\jslock.h"\ ".\jsnum.h"\ ".\jsobj.h"\ ".\jsopcode.h"\ ".\jsparse.h"\ ".\jsscan.h"\ ".\jsscope.h"\ ".\jsscript.h"\ ".\jsstr.h"\ ".\jstypes.h"\ ".\jsutil.h"\ ".\jsxml.h"\ ".\jsprf.h"\ {$(INCLUDE)}"\sys\types.h"\ NODEP_CPP_JSXML=\ ".\jsautocfg.h"\ ".\prcvar.h"\ ".\prlock.h"\ "$(INTDIR)\jsxml.obj" : $(SOURCE) $(DEP_CPP_JSXML) "$(INTDIR)" !ENDIF # End Source File ################################################################################ # Begin Source File SOURCE=.\prmjtime.c !IF "$(CFG)" == "js - Win32 Release" DEP_CPP_PRMJT=\ ".\jscompat.h"\ ".\jscpucfg.h"\ ".\jslong.h"\ ".\jsosdep.h"\ ".\jsotypes.h"\ ".\jsprf.h"\ ".\jstypes.h"\ ".\prmjtime.h"\ {$(INCLUDE)}"\sys\TIMEB.H"\ {$(INCLUDE)}"\sys\types.h"\ NODEP_CPP_PRMJT=\ ".\jsautocfg.h"\ "$(INTDIR)\prmjtime.obj" : $(SOURCE) $(DEP_CPP_PRMJT) "$(INTDIR)" !ELSEIF "$(CFG)" == "js - Win32 Debug" DEP_CPP_PRMJT=\ ".\jscompat.h"\ ".\jscpucfg.h"\ ".\jslong.h"\ ".\jsosdep.h"\ ".\jsotypes.h"\ ".\jsprf.h"\ ".\jstypes.h"\ ".\prmjtime.h"\ {$(INCLUDE)}"\sys\TIMEB.H"\ {$(INCLUDE)}"\sys\types.h"\ NODEP_CPP_PRMJT=\ ".\jsautocfg.h"\ "$(INTDIR)\prmjtime.obj" : $(SOURCE) $(DEP_CPP_PRMJT) "$(INTDIR)" !ENDIF # End Source File ################################################################################ # Begin Project Dependency # Project_Dep_Name "fdlibm" !IF "$(CFG)" == "js - Win32 Debug" "fdlibm - Win32 Debug" : @$(MAKE) /nologo /$(MAKEFLAGS) /F ".\js.mak" CFG="fdlibm - Win32 Debug" !ELSEIF "$(CFG)" == "js - Win32 Release" "fdlibm - Win32 Release" : @$(MAKE) /nologo /$(MAKEFLAGS) /F ".\js.mak" CFG="fdlibm - Win32 Release" !ENDIF # End Project Dependency # End Target ################################################################################ # Begin Target # Name "jsshell - Win32 Release" # Name "jsshell - Win32 Debug" !IF "$(CFG)" == "jsshell - Win32 Release" !ELSEIF "$(CFG)" == "jsshell - Win32 Debug" !ENDIF ################################################################################ # Begin Source File SOURCE=.\js.c DEP_CPP_JS_C42=\ ".\js.msg"\ ".\jsapi.h"\ ".\jsarena.h"\ ".\jsatom.h"\ ".\jsclist.h"\ ".\jscntxt.h"\ ".\jscompat.h"\ ".\jsconfig.h"\ ".\jscpucfg.h"\ ".\jsdbgapi.h"\ ".\jsemit.h"\ ".\jsfun.h"\ ".\jsgc.h"\ ".\jshash.h"\ ".\jsinterp.h"\ ".\jslock.h"\ ".\jslong.h"\ ".\jsobj.h"\ ".\jsopcode.h"\ ".\jsopcode.tbl"\ ".\jsosdep.h"\ ".\jsotypes.h"\ ".\jsparse.h"\ ".\jsprf.h"\ ".\jsprvtd.h"\ ".\jspubtd.h"\ ".\jsregexp.h"\ ".\jsscan.h"\ ".\jsscope.h"\ ".\jsscript.h"\ ".\jsshell.msg"\ ".\jsstddef.h"\ ".\jsstr.h"\ ".\jstypes.h"\ ".\jsutil.h"\ {$(INCLUDE)}"\sys\types.h"\ NODEP_CPP_JS_C42=\ ".\jsautocfg.h"\ ".\jsdb.h"\ ".\jsdebug.h"\ ".\jsdjava.h"\ ".\jsjava.h"\ ".\jsperl.h"\ ".\prcvar.h"\ ".\prlock.h"\ "$(INTDIR)\js.obj" : $(SOURCE) $(DEP_CPP_JS_C42) "$(INTDIR)" # End Source File ################################################################################ # Begin Project Dependency # Project_Dep_Name "jskwgen" !IF "$(CFG)" == "js - Win32 Release" "jskwgen - Win32 Release" : @$(MAKE) /nologo /$(MAKEFLAGS) /F ".\js.mak" CFG="jskwgen - Win32 Release" !ELSEIF "$(CFG)" == "js - Win32 Debug" "jskwgen - Win32 Debug" : @$(MAKE) /nologo /$(MAKEFLAGS) /F ".\js.mak" CFG="jskwgen - Win32 Debug" !ENDIF # End Project Dependency # End Target ################################################################################ # Begin Project Dependency # Project_Dep_Name "js" !IF "$(CFG)" == "jsshell - Win32 Release" "js - Win32 Release" : @$(MAKE) /nologo /$(MAKEFLAGS) /F ".\js.mak" CFG="js - Win32 Release" !ELSEIF "$(CFG)" == "jsshell - Win32 Debug" "js - Win32 Debug" : @$(MAKE) /nologo /$(MAKEFLAGS) /F ".\js.mak" CFG="js - Win32 Debug" !ENDIF # End Project Dependency # End Target ################################################################################ # Begin Target # Name "fdlibm - Win32 Release" # Name "fdlibm - Win32 Debug" !IF "$(CFG)" == "fdlibm - Win32 Release" !ELSEIF "$(CFG)" == "fdlibm - Win32 Debug" !ENDIF ################################################################################ # Begin Source File SOURCE=.\fdlibm\w_atan2.c !IF "$(CFG)" == "fdlibm - Win32 Release" DEP_CPP_W_ATA=\ ".\fdlibm\fdlibm.h"\ "$(INTDIR)\w_atan2.obj" : $(SOURCE) $(DEP_CPP_W_ATA) "$(INTDIR)" $(CPP) $(CPP_PROJ) $(SOURCE) !ELSEIF "$(CFG)" == "fdlibm - Win32 Debug" DEP_CPP_W_ATA=\ ".\fdlibm\fdlibm.h"\ "$(INTDIR)\w_atan2.obj" : $(SOURCE) $(DEP_CPP_W_ATA) "$(INTDIR)" $(CPP) $(CPP_PROJ) $(SOURCE) !ENDIF # End Source File ################################################################################ # Begin Source File SOURCE=.\fdlibm\s_copysign.c !IF "$(CFG)" == "fdlibm - Win32 Release" DEP_CPP_S_COP=\ ".\fdlibm\fdlibm.h"\ "$(INTDIR)\s_copysign.obj" : $(SOURCE) $(DEP_CPP_S_COP) "$(INTDIR)" $(CPP) $(CPP_PROJ) $(SOURCE) !ELSEIF "$(CFG)" == "fdlibm - Win32 Debug" DEP_CPP_S_COP=\ ".\fdlibm\fdlibm.h"\ "$(INTDIR)\s_copysign.obj" : $(SOURCE) $(DEP_CPP_S_COP) "$(INTDIR)" $(CPP) $(CPP_PROJ) $(SOURCE) !ENDIF # End Source File ################################################################################ # Begin Source File SOURCE=.\fdlibm\w_pow.c !IF "$(CFG)" == "fdlibm - Win32 Release" DEP_CPP_W_POW=\ ".\fdlibm\fdlibm.h"\ "$(INTDIR)\w_pow.obj" : $(SOURCE) $(DEP_CPP_W_POW) "$(INTDIR)" $(CPP) $(CPP_PROJ) $(SOURCE) !ELSEIF "$(CFG)" == "fdlibm - Win32 Debug" DEP_CPP_W_POW=\ ".\fdlibm\fdlibm.h"\ "$(INTDIR)\w_pow.obj" : $(SOURCE) $(DEP_CPP_W_POW) "$(INTDIR)" $(CPP) $(CPP_PROJ) $(SOURCE) !ENDIF # End Source File ################################################################################ # Begin Source File SOURCE=.\fdlibm\e_pow.c !IF "$(CFG)" == "fdlibm - Win32 Release" DEP_CPP_E_POW=\ ".\fdlibm\fdlibm.h"\ "$(INTDIR)\e_pow.obj" : $(SOURCE) $(DEP_CPP_E_POW) "$(INTDIR)" $(CPP) $(CPP_PROJ) $(SOURCE) !ELSEIF "$(CFG)" == "fdlibm - Win32 Debug" DEP_CPP_E_POW=\ ".\fdlibm\fdlibm.h"\ "$(INTDIR)\e_pow.obj" : $(SOURCE) $(DEP_CPP_E_POW) "$(INTDIR)" $(CPP) $(CPP_PROJ) $(SOURCE) !ENDIF # End Source File ################################################################################ # Begin Source File SOURCE=.\fdlibm\k_standard.c !IF "$(CFG)" == "fdlibm - Win32 Release" DEP_CPP_K_STA=\ ".\fdlibm\fdlibm.h"\ "$(INTDIR)\k_standard.obj" : $(SOURCE) $(DEP_CPP_K_STA) "$(INTDIR)" $(CPP) $(CPP_PROJ) $(SOURCE) !ELSEIF "$(CFG)" == "fdlibm - Win32 Debug" DEP_CPP_K_STA=\ ".\fdlibm\fdlibm.h"\ "$(INTDIR)\k_standard.obj" : $(SOURCE) $(DEP_CPP_K_STA) "$(INTDIR)" $(CPP) $(CPP_PROJ) $(SOURCE) !ENDIF # End Source File ################################################################################ # Begin Source File SOURCE=.\fdlibm\e_atan2.c !IF "$(CFG)" == "fdlibm - Win32 Release" DEP_CPP_E_ATA=\ ".\fdlibm\fdlibm.h"\ "$(INTDIR)\e_atan2.obj" : $(SOURCE) $(DEP_CPP_E_ATA) "$(INTDIR)" $(CPP) $(CPP_PROJ) $(SOURCE) !ELSEIF "$(CFG)" == "fdlibm - Win32 Debug" DEP_CPP_E_ATA=\ ".\fdlibm\fdlibm.h"\ "$(INTDIR)\e_atan2.obj" : $(SOURCE) $(DEP_CPP_E_ATA) "$(INTDIR)" $(CPP) $(CPP_PROJ) $(SOURCE) !ENDIF # End Source File ################################################################################ # Begin Source File SOURCE=.\fdlibm\s_isnan.c !IF "$(CFG)" == "fdlibm - Win32 Release" DEP_CPP_S_ISN=\ ".\fdlibm\fdlibm.h"\ "$(INTDIR)\s_isnan.obj" : $(SOURCE) $(DEP_CPP_S_ISN) "$(INTDIR)" $(CPP) $(CPP_PROJ) $(SOURCE) !ELSEIF "$(CFG)" == "fdlibm - Win32 Debug" DEP_CPP_S_ISN=\ ".\fdlibm\fdlibm.h"\ "$(INTDIR)\s_isnan.obj" : $(SOURCE) $(DEP_CPP_S_ISN) "$(INTDIR)" $(CPP) $(CPP_PROJ) $(SOURCE) !ENDIF # End Source File ################################################################################ # Begin Source File SOURCE=.\fdlibm\s_fabs.c !IF "$(CFG)" == "fdlibm - Win32 Release" DEP_CPP_S_FAB=\ ".\fdlibm\fdlibm.h"\ "$(INTDIR)\s_fabs.obj" : $(SOURCE) $(DEP_CPP_S_FAB) "$(INTDIR)" $(CPP) $(CPP_PROJ) $(SOURCE) !ELSEIF "$(CFG)" == "fdlibm - Win32 Debug" DEP_CPP_S_FAB=\ ".\fdlibm\fdlibm.h"\ "$(INTDIR)\s_fabs.obj" : $(SOURCE) $(DEP_CPP_S_FAB) "$(INTDIR)" $(CPP) $(CPP_PROJ) $(SOURCE) !ENDIF # End Source File ################################################################################ # Begin Source File SOURCE=.\fdlibm\w_sqrt.c !IF "$(CFG)" == "fdlibm - Win32 Release" DEP_CPP_W_SQR=\ ".\fdlibm\fdlibm.h"\ "$(INTDIR)\w_sqrt.obj" : $(SOURCE) $(DEP_CPP_W_SQR) "$(INTDIR)" $(CPP) $(CPP_PROJ) $(SOURCE) !ELSEIF "$(CFG)" == "fdlibm - Win32 Debug" DEP_CPP_W_SQR=\ ".\fdlibm\fdlibm.h"\ "$(INTDIR)\w_sqrt.obj" : $(SOURCE) $(DEP_CPP_W_SQR) "$(INTDIR)" $(CPP) $(CPP_PROJ) $(SOURCE) !ENDIF # End Source File ################################################################################ # Begin Source File SOURCE=.\fdlibm\s_scalbn.c !IF "$(CFG)" == "fdlibm - Win32 Release" DEP_CPP_S_SCA=\ ".\fdlibm\fdlibm.h"\ "$(INTDIR)\s_scalbn.obj" : $(SOURCE) $(DEP_CPP_S_SCA) "$(INTDIR)" $(CPP) $(CPP_PROJ) $(SOURCE) !ELSEIF "$(CFG)" == "fdlibm - Win32 Debug" DEP_CPP_S_SCA=\ ".\fdlibm\fdlibm.h"\ "$(INTDIR)\s_scalbn.obj" : $(SOURCE) $(DEP_CPP_S_SCA) "$(INTDIR)" $(CPP) $(CPP_PROJ) $(SOURCE) !ENDIF # End Source File ################################################################################ # Begin Source File SOURCE=.\fdlibm\e_sqrt.c !IF "$(CFG)" == "fdlibm - Win32 Release" DEP_CPP_E_SQR=\ ".\fdlibm\fdlibm.h"\ "$(INTDIR)\e_sqrt.obj" : $(SOURCE) $(DEP_CPP_E_SQR) "$(INTDIR)" $(CPP) $(CPP_PROJ) $(SOURCE) !ELSEIF "$(CFG)" == "fdlibm - Win32 Debug" DEP_CPP_E_SQR=\ ".\fdlibm\fdlibm.h"\ "$(INTDIR)\e_sqrt.obj" : $(SOURCE) $(DEP_CPP_E_SQR) "$(INTDIR)" $(CPP) $(CPP_PROJ) $(SOURCE) !ENDIF # End Source File ################################################################################ # Begin Source File SOURCE=.\fdlibm\s_rint.c !IF "$(CFG)" == "fdlibm - Win32 Release" DEP_CPP_S_RIN=\ ".\fdlibm\fdlibm.h"\ "$(INTDIR)\s_rint.obj" : $(SOURCE) $(DEP_CPP_S_RIN) "$(INTDIR)" $(CPP) $(CPP_PROJ) $(SOURCE) !ELSEIF "$(CFG)" == "fdlibm - Win32 Debug" DEP_CPP_S_RIN=\ ".\fdlibm\fdlibm.h"\ "$(INTDIR)\s_rint.obj" : $(SOURCE) $(DEP_CPP_S_RIN) "$(INTDIR)" $(CPP) $(CPP_PROJ) $(SOURCE) !ENDIF # End Source File ################################################################################ # Begin Source File SOURCE=.\fdlibm\s_atan.c !IF "$(CFG)" == "fdlibm - Win32 Release" DEP_CPP_S_ATA=\ ".\fdlibm\fdlibm.h"\ "$(INTDIR)\s_atan.obj" : $(SOURCE) $(DEP_CPP_S_ATA) "$(INTDIR)" $(CPP) $(CPP_PROJ) $(SOURCE) !ELSEIF "$(CFG)" == "fdlibm - Win32 Debug" DEP_CPP_S_ATA=\ ".\fdlibm\fdlibm.h"\ "$(INTDIR)\s_atan.obj" : $(SOURCE) $(DEP_CPP_S_ATA) "$(INTDIR)" $(CPP) $(CPP_PROJ) $(SOURCE) !ENDIF # End Source File ################################################################################ # Begin Source File SOURCE=.\fdlibm\s_finite.c !IF "$(CFG)" == "fdlibm - Win32 Release" DEP_CPP_S_FIN=\ ".\fdlibm\fdlibm.h"\ "$(INTDIR)\s_finite.obj" : $(SOURCE) $(DEP_CPP_S_FIN) "$(INTDIR)" $(CPP) $(CPP_PROJ) $(SOURCE) !ELSEIF "$(CFG)" == "fdlibm - Win32 Debug" DEP_CPP_S_FIN=\ ".\fdlibm\fdlibm.h"\ "$(INTDIR)\s_finite.obj" : $(SOURCE) $(DEP_CPP_S_FIN) "$(INTDIR)" $(CPP) $(CPP_PROJ) $(SOURCE) !ENDIF # End Source File ################################################################################ # Begin Source File SOURCE=.\fdlibm\s_matherr.c !IF "$(CFG)" == "fdlibm - Win32 Release" DEP_CPP_S_MAT=\ ".\fdlibm\fdlibm.h"\ "$(INTDIR)\s_matherr.obj" : $(SOURCE) $(DEP_CPP_S_MAT) "$(INTDIR)" $(CPP) $(CPP_PROJ) $(SOURCE) !ELSEIF "$(CFG)" == "fdlibm - Win32 Debug" DEP_CPP_S_MAT=\ ".\fdlibm\fdlibm.h"\ "$(INTDIR)\s_matherr.obj" : $(SOURCE) $(DEP_CPP_S_MAT) "$(INTDIR)" $(CPP) $(CPP_PROJ) $(SOURCE) !ENDIF # End Source File # End Target # End Project ################################################################################ pacparser-1.4.5/src/spidermonkey/js/src/js.mdp000066400000000000000000000430021464010763600213300ustar00rootroot00000000000000JSG0ZÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿàÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿN-Y#ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿüÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ vcbks40.mvbûÙ&e½¸0õ bûÙ&e½¸0õ Ë]@ Control-C@ Control-Break€Datatype MisalignmentÀAccess ViolationÀ In Page ErrorÀ No MemoryÀIllegal Instruction%ÀNoncontinuable Exception&ÀInvalid DispositionŒÀArray Bounds ExceededÀFloat Denormal OperandŽÀFloat Divide by ZeroÀFloat Inexact ResultÀFloat Invalid Operation‘ÀFloat Overflow’ÀFloat Stack Check“ÀFloat Underflow”ÀInteger Divide by Zero•ÀInteger Overflow–ÀPrivileged InstructionýÀStack OverflowBÀDLL Initialization FailedcsmàMicrosoft C++ ExceptionWatch1Watch2Watch3Watch4HMLJL½¸0õ}A¸0õpacparser-1.4.5/src/spidermonkey/js/src/js.msg000066400000000000000000000571421464010763600213500ustar00rootroot00000000000000/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- * * ***** BEGIN LICENSE BLOCK ***** * Version: MPL 1.1/GPL 2.0/LGPL 2.1 * * The contents of this file are subject to the Mozilla Public License Version * 1.1 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * http://www.mozilla.org/MPL/ * * Software distributed under the License is distributed on an "AS IS" basis, * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License * for the specific language governing rights and limitations under the * License. * * The Original Code is Mozilla Communicator client code, released * March 31, 1998. * * The Initial Developer of the Original Code is * Netscape Communications Corporation. * Portions created by the Initial Developer are Copyright (C) 1998 * the Initial Developer. All Rights Reserved. * * Contributor(s): * * Alternatively, the contents of this file may be used under the terms of * either of the GNU General Public License Version 2 or later (the "GPL"), * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), * in which case the provisions of the GPL or the LGPL are applicable instead * of those above. If you wish to allow use of your version of this file only * under the terms of either the GPL or the LGPL, and not to allow others to * use your version of this file under the terms of the MPL, indicate your * decision by deleting the provisions above and replace them with the notice * and other provisions required by the GPL or the LGPL. If you do not delete * the provisions above, a recipient may use your version of this file under * the terms of any one of the MPL, the GPL or the LGPL. * * ***** END LICENSE BLOCK ***** */ /* * This is the JavaScript error message file. * * The format for each JS error message is: * * MSG_DEF(, , , , * ) * * where ; * is a legal C identifer that will be used in the * JS engine source. * * is an unique integral value identifying this error. * * is an integer literal specifying the total number of * replaceable arguments in the following format string. * * is an exception index from the enum in jsexn.c; * JSEXN_NONE for none. The given exception index will be raised by the * engine when the corresponding error occurs. * * is a string literal, optionally containing sequences * {X} where X is an integer representing the argument number that will * be replaced with a string value when the error is reported. * * e.g. * * MSG_DEF(JSMSG_NOT_A_SUBSPECIES, 73, JSEXN_NONE, 2, * "{0} is not a member of the {1} family") * * can be used: * * JS_ReportErrorNumber(JSMSG_NOT_A_SUBSPECIES, "Rhino", "Monkey"); * * to report: * * "Rhino is not a member of the Monkey family" * * Before adding a new MSG_DEF at the end, look for JSMSG_UNUSED free * index placeholders in the middle of the list. */ MSG_DEF(JSMSG_NOT_AN_ERROR, 0, 0, JSEXN_NONE, "") MSG_DEF(JSMSG_NOT_DEFINED, 1, 1, JSEXN_REFERENCEERR, "{0} is not defined") MSG_DEF(JSMSG_INACTIVE, 2, 0, JSEXN_INTERNALERR, "nothing active on context") MSG_DEF(JSMSG_MORE_ARGS_NEEDED, 3, 3, JSEXN_TYPEERR, "{0} requires more than {1} argument{2}") MSG_DEF(JSMSG_BAD_CHAR, 4, 1, JSEXN_INTERNALERR, "invalid format character {0}") MSG_DEF(JSMSG_BAD_TYPE, 5, 1, JSEXN_TYPEERR, "unknown type {0}") MSG_DEF(JSMSG_CANT_LOCK, 6, 0, JSEXN_INTERNALERR, "can't lock memory") MSG_DEF(JSMSG_CANT_UNLOCK, 7, 0, JSEXN_INTERNALERR, "can't unlock memory") MSG_DEF(JSMSG_INCOMPATIBLE_PROTO, 8, 3, JSEXN_TYPEERR, "{0}.prototype.{1} called on incompatible {2}") MSG_DEF(JSMSG_NO_CONSTRUCTOR, 9, 1, JSEXN_TYPEERR, "{0} has no constructor") MSG_DEF(JSMSG_CANT_ALIAS, 10, 3, JSEXN_TYPEERR, "can't alias {0} to {1} in class {2}") MSG_DEF(JSMSG_NOT_SCRIPTED_FUNCTION, 11, 1, JSEXN_TYPEERR, "{0} is not a scripted function") MSG_DEF(JSMSG_BAD_SORT_ARG, 12, 0, JSEXN_TYPEERR, "invalid Array.prototype.sort argument") MSG_DEF(JSMSG_BAD_ATOMIC_NUMBER, 13, 1, JSEXN_INTERNALERR, "internal error: no index for atom {0}") MSG_DEF(JSMSG_TOO_MANY_LITERALS, 14, 0, JSEXN_INTERNALERR, "too many literals") MSG_DEF(JSMSG_CANT_WATCH, 15, 1, JSEXN_TYPEERR, "can't watch non-native objects of class {0}") MSG_DEF(JSMSG_STACK_UNDERFLOW, 16, 2, JSEXN_INTERNALERR, "internal error compiling {0}: stack underflow at pc {1}") MSG_DEF(JSMSG_NEED_DIET, 17, 1, JSEXN_INTERNALERR, "{0} too large") MSG_DEF(JSMSG_TOO_MANY_LOCAL_ROOTS, 18, 0, JSEXN_ERR, "out of local root space") MSG_DEF(JSMSG_READ_ONLY, 19, 1, JSEXN_ERR, "{0} is read-only") MSG_DEF(JSMSG_BAD_FORMAL, 20, 0, JSEXN_SYNTAXERR, "malformed formal parameter") MSG_DEF(JSMSG_BAD_ITERATOR, 21, 3, JSEXN_TYPEERR, "{0} has invalid {1} value {2}") MSG_DEF(JSMSG_NOT_FUNCTION, 22, 1, JSEXN_TYPEERR, "{0} is not a function") MSG_DEF(JSMSG_NOT_CONSTRUCTOR, 23, 1, JSEXN_TYPEERR, "{0} is not a constructor") MSG_DEF(JSMSG_STACK_OVERFLOW, 24, 1, JSEXN_INTERNALERR, "stack overflow in {0}") MSG_DEF(JSMSG_NOT_EXPORTED, 25, 1, JSEXN_TYPEERR, "{0} is not exported") MSG_DEF(JSMSG_OVER_RECURSED, 26, 0, JSEXN_INTERNALERR, "too much recursion") MSG_DEF(JSMSG_IN_NOT_OBJECT, 27, 1, JSEXN_TYPEERR, "invalid 'in' operand {0}") MSG_DEF(JSMSG_BAD_NEW_RESULT, 28, 1, JSEXN_TYPEERR, "invalid new expression result {0}") MSG_DEF(JSMSG_BAD_SHARP_DEF, 29, 1, JSEXN_ERR, "invalid sharp variable definition #{0}=") MSG_DEF(JSMSG_BAD_SHARP_USE, 30, 1, JSEXN_ERR, "invalid sharp variable use #{0}#") MSG_DEF(JSMSG_BAD_INSTANCEOF_RHS, 31, 1, JSEXN_TYPEERR, "invalid 'instanceof' operand {0}") MSG_DEF(JSMSG_BAD_BYTECODE, 32, 1, JSEXN_INTERNALERR, "unimplemented JavaScript bytecode {0}") MSG_DEF(JSMSG_BAD_RADIX, 33, 1, JSEXN_ERR, "illegal radix {0}") MSG_DEF(JSMSG_PAREN_BEFORE_LET, 34, 0, JSEXN_SYNTAXERR, "missing ( before let head") MSG_DEF(JSMSG_CANT_CONVERT, 35, 1, JSEXN_ERR, "can't convert {0} to an integer") MSG_DEF(JSMSG_CYCLIC_VALUE, 36, 1, JSEXN_ERR, "cyclic {0} value") MSG_DEF(JSMSG_PERMANENT, 37, 1, JSEXN_ERR, "{0} is permanent") MSG_DEF(JSMSG_CANT_CONVERT_TO, 38, 2, JSEXN_TYPEERR, "can't convert {0} to {1}") MSG_DEF(JSMSG_NO_PROPERTIES, 39, 1, JSEXN_TYPEERR, "{0} has no properties") MSG_DEF(JSMSG_CANT_FIND_CLASS, 40, 1, JSEXN_TYPEERR, "can't find class id {0}") MSG_DEF(JSMSG_CANT_XDR_CLASS, 41, 1, JSEXN_TYPEERR, "can't XDR class {0}") MSG_DEF(JSMSG_BYTECODE_TOO_BIG, 42, 2, JSEXN_INTERNALERR, "bytecode {0} too large (limit {1})") MSG_DEF(JSMSG_UNKNOWN_FORMAT, 43, 1, JSEXN_INTERNALERR, "unknown bytecode format {0}") MSG_DEF(JSMSG_TOO_MANY_CON_ARGS, 44, 0, JSEXN_SYNTAXERR, "too many constructor arguments") MSG_DEF(JSMSG_TOO_MANY_FUN_ARGS, 45, 0, JSEXN_SYNTAXERR, "too many function arguments") MSG_DEF(JSMSG_BAD_QUANTIFIER, 46, 1, JSEXN_SYNTAXERR, "invalid quantifier {0}") MSG_DEF(JSMSG_MIN_TOO_BIG, 47, 1, JSEXN_SYNTAXERR, "overlarge minimum {0}") MSG_DEF(JSMSG_MAX_TOO_BIG, 48, 1, JSEXN_SYNTAXERR, "overlarge maximum {0}") MSG_DEF(JSMSG_OUT_OF_ORDER, 49, 1, JSEXN_SYNTAXERR, "maximum {0} less than minimum") MSG_DEF(JSMSG_BAD_DESTRUCT_DECL, 50, 0, JSEXN_SYNTAXERR, "missing = in destructuring declaration") MSG_DEF(JSMSG_BAD_DESTRUCT_ASS, 51, 0, JSEXN_SYNTAXERR, "invalid destructuring assignment operator") MSG_DEF(JSMSG_PAREN_AFTER_LET, 52, 0, JSEXN_SYNTAXERR, "missing ) after let head") MSG_DEF(JSMSG_CURLY_AFTER_LET, 53, 0, JSEXN_SYNTAXERR, "missing } after let block") MSG_DEF(JSMSG_MISSING_PAREN, 54, 0, JSEXN_SYNTAXERR, "unterminated parenthetical") MSG_DEF(JSMSG_UNTERM_CLASS, 55, 1, JSEXN_SYNTAXERR, "unterminated character class {0}") MSG_DEF(JSMSG_TRAILING_SLASH, 56, 0, JSEXN_SYNTAXERR, "trailing \\ in regular expression") MSG_DEF(JSMSG_BAD_CLASS_RANGE, 57, 0, JSEXN_SYNTAXERR, "invalid range in character class") MSG_DEF(JSMSG_BAD_FLAG, 58, 1, JSEXN_SYNTAXERR, "invalid regular expression flag {0}") MSG_DEF(JSMSG_NO_INPUT, 59, 3, JSEXN_SYNTAXERR, "no input for /{0}/{1}{2}") MSG_DEF(JSMSG_CANT_OPEN, 60, 2, JSEXN_ERR, "can't open {0}: {1}") MSG_DEF(JSMSG_BAD_STRING_MASK, 61, 1, JSEXN_ERR, "invalid string escape mask {0}") MSG_DEF(JSMSG_UNMATCHED_RIGHT_PAREN, 62, 0, JSEXN_SYNTAXERR, "unmatched ) in regular expression") MSG_DEF(JSMSG_END_OF_DATA, 63, 0, JSEXN_INTERNALERR, "unexpected end of data") MSG_DEF(JSMSG_SEEK_BEYOND_START, 64, 0, JSEXN_INTERNALERR, "illegal seek beyond start") MSG_DEF(JSMSG_SEEK_BEYOND_END, 65, 0, JSEXN_INTERNALERR, "illegal seek beyond end") MSG_DEF(JSMSG_END_SEEK, 66, 0, JSEXN_INTERNALERR, "illegal end-based seek") MSG_DEF(JSMSG_WHITHER_WHENCE, 67, 1, JSEXN_INTERNALERR, "unknown seek whence: {0}") MSG_DEF(JSMSG_BAD_SCRIPT_MAGIC, 68, 0, JSEXN_INTERNALERR, "bad script XDR magic number") MSG_DEF(JSMSG_PAREN_BEFORE_FORMAL, 69, 0, JSEXN_SYNTAXERR, "missing ( before formal parameters") MSG_DEF(JSMSG_MISSING_FORMAL, 70, 0, JSEXN_SYNTAXERR, "missing formal parameter") MSG_DEF(JSMSG_PAREN_AFTER_FORMAL, 71, 0, JSEXN_SYNTAXERR, "missing ) after formal parameters") MSG_DEF(JSMSG_CURLY_BEFORE_BODY, 72, 0, JSEXN_SYNTAXERR, "missing { before function body") MSG_DEF(JSMSG_CURLY_AFTER_BODY, 73, 0, JSEXN_SYNTAXERR, "missing } after function body") MSG_DEF(JSMSG_PAREN_BEFORE_COND, 74, 0, JSEXN_SYNTAXERR, "missing ( before condition") MSG_DEF(JSMSG_PAREN_AFTER_COND, 75, 0, JSEXN_SYNTAXERR, "missing ) after condition") MSG_DEF(JSMSG_NO_IMPORT_NAME, 76, 0, JSEXN_SYNTAXERR, "missing name in import statement") MSG_DEF(JSMSG_NAME_AFTER_DOT, 77, 0, JSEXN_SYNTAXERR, "missing name after . operator") MSG_DEF(JSMSG_BRACKET_IN_INDEX, 78, 0, JSEXN_SYNTAXERR, "missing ] in index expression") MSG_DEF(JSMSG_NO_EXPORT_NAME, 79, 0, JSEXN_SYNTAXERR, "missing name in export statement") MSG_DEF(JSMSG_PAREN_BEFORE_SWITCH, 80, 0, JSEXN_SYNTAXERR, "missing ( before switch expression") MSG_DEF(JSMSG_PAREN_AFTER_SWITCH, 81, 0, JSEXN_SYNTAXERR, "missing ) after switch expression") MSG_DEF(JSMSG_CURLY_BEFORE_SWITCH, 82, 0, JSEXN_SYNTAXERR, "missing { before switch body") MSG_DEF(JSMSG_COLON_AFTER_CASE, 83, 0, JSEXN_SYNTAXERR, "missing : after case label") MSG_DEF(JSMSG_WHILE_AFTER_DO, 84, 0, JSEXN_SYNTAXERR, "missing while after do-loop body") MSG_DEF(JSMSG_PAREN_AFTER_FOR, 85, 0, JSEXN_SYNTAXERR, "missing ( after for") MSG_DEF(JSMSG_SEMI_AFTER_FOR_INIT, 86, 0, JSEXN_SYNTAXERR, "missing ; after for-loop initializer") MSG_DEF(JSMSG_SEMI_AFTER_FOR_COND, 87, 0, JSEXN_SYNTAXERR, "missing ; after for-loop condition") MSG_DEF(JSMSG_PAREN_AFTER_FOR_CTRL, 88, 0, JSEXN_SYNTAXERR, "missing ) after for-loop control") MSG_DEF(JSMSG_CURLY_BEFORE_TRY, 89, 0, JSEXN_SYNTAXERR, "missing { before try block") MSG_DEF(JSMSG_CURLY_AFTER_TRY, 90, 0, JSEXN_SYNTAXERR, "missing } after try block") MSG_DEF(JSMSG_PAREN_BEFORE_CATCH, 91, 0, JSEXN_SYNTAXERR, "missing ( before catch") MSG_DEF(JSMSG_CATCH_IDENTIFIER, 92, 0, JSEXN_SYNTAXERR, "missing identifier in catch") MSG_DEF(JSMSG_PAREN_AFTER_CATCH, 93, 0, JSEXN_SYNTAXERR, "missing ) after catch") MSG_DEF(JSMSG_CURLY_BEFORE_CATCH, 94, 0, JSEXN_SYNTAXERR, "missing { before catch block") MSG_DEF(JSMSG_CURLY_AFTER_CATCH, 95, 0, JSEXN_SYNTAXERR, "missing } after catch block") MSG_DEF(JSMSG_CURLY_BEFORE_FINALLY, 96, 0, JSEXN_SYNTAXERR, "missing { before finally block") MSG_DEF(JSMSG_CURLY_AFTER_FINALLY, 97, 0, JSEXN_SYNTAXERR, "missing } after finally block") MSG_DEF(JSMSG_CATCH_OR_FINALLY, 98, 0, JSEXN_SYNTAXERR, "missing catch or finally after try") MSG_DEF(JSMSG_PAREN_BEFORE_WITH, 99, 0, JSEXN_SYNTAXERR, "missing ( before with-statement object") MSG_DEF(JSMSG_PAREN_AFTER_WITH, 100, 0, JSEXN_SYNTAXERR, "missing ) after with-statement object") MSG_DEF(JSMSG_CURLY_IN_COMPOUND, 101, 0, JSEXN_SYNTAXERR, "missing } in compound statement") MSG_DEF(JSMSG_NO_VARIABLE_NAME, 102, 0, JSEXN_SYNTAXERR, "missing variable name") MSG_DEF(JSMSG_COLON_IN_COND, 103, 0, JSEXN_SYNTAXERR, "missing : in conditional expression") MSG_DEF(JSMSG_PAREN_AFTER_ARGS, 104, 0, JSEXN_SYNTAXERR, "missing ) after argument list") MSG_DEF(JSMSG_BRACKET_AFTER_LIST, 105, 0, JSEXN_SYNTAXERR, "missing ] after element list") MSG_DEF(JSMSG_COLON_AFTER_ID, 106, 0, JSEXN_SYNTAXERR, "missing : after property id") MSG_DEF(JSMSG_CURLY_AFTER_LIST, 107, 0, JSEXN_SYNTAXERR, "missing } after property list") MSG_DEF(JSMSG_PAREN_IN_PAREN, 108, 0, JSEXN_SYNTAXERR, "missing ) in parenthetical") MSG_DEF(JSMSG_SEMI_BEFORE_STMNT, 109, 0, JSEXN_SYNTAXERR, "missing ; before statement") MSG_DEF(JSMSG_NO_RETURN_VALUE, 110, 1, JSEXN_TYPEERR, "function {0} does not always return a value") MSG_DEF(JSMSG_DUPLICATE_FORMAL, 111, 1, JSEXN_TYPEERR, "duplicate formal argument {0}") MSG_DEF(JSMSG_EQUAL_AS_ASSIGN, 112, 1, JSEXN_SYNTAXERR, "test for equality (==) mistyped as assignment (=)?{0}") MSG_DEF(JSMSG_BAD_IMPORT, 113, 0, JSEXN_SYNTAXERR, "invalid import expression") MSG_DEF(JSMSG_TOO_MANY_DEFAULTS, 114, 0, JSEXN_SYNTAXERR, "more than one switch default") MSG_DEF(JSMSG_TOO_MANY_CASES, 115, 0, JSEXN_INTERNALERR, "too many switch cases") MSG_DEF(JSMSG_BAD_SWITCH, 116, 0, JSEXN_SYNTAXERR, "invalid switch statement") MSG_DEF(JSMSG_BAD_FOR_LEFTSIDE, 117, 0, JSEXN_SYNTAXERR, "invalid for/in left-hand side") MSG_DEF(JSMSG_CATCH_AFTER_GENERAL, 118, 0, JSEXN_SYNTAXERR, "catch after unconditional catch") MSG_DEF(JSMSG_CATCH_WITHOUT_TRY, 119, 0, JSEXN_SYNTAXERR, "catch without try") MSG_DEF(JSMSG_FINALLY_WITHOUT_TRY, 120, 0, JSEXN_SYNTAXERR, "finally without try") MSG_DEF(JSMSG_LABEL_NOT_FOUND, 121, 0, JSEXN_SYNTAXERR, "label not found") MSG_DEF(JSMSG_TOUGH_BREAK, 122, 0, JSEXN_SYNTAXERR, "invalid break") MSG_DEF(JSMSG_BAD_CONTINUE, 123, 0, JSEXN_SYNTAXERR, "invalid continue") MSG_DEF(JSMSG_BAD_RETURN_OR_YIELD, 124, 1, JSEXN_SYNTAXERR, "{0} not in function") MSG_DEF(JSMSG_BAD_LABEL, 125, 0, JSEXN_SYNTAXERR, "invalid label") MSG_DEF(JSMSG_DUPLICATE_LABEL, 126, 0, JSEXN_SYNTAXERR, "duplicate label") MSG_DEF(JSMSG_VAR_HIDES_ARG, 127, 1, JSEXN_TYPEERR, "variable {0} hides argument") MSG_DEF(JSMSG_BAD_VAR_INIT, 128, 0, JSEXN_SYNTAXERR, "invalid variable initialization") MSG_DEF(JSMSG_BAD_LEFTSIDE_OF_ASS, 129, 0, JSEXN_SYNTAXERR, "invalid assignment left-hand side") MSG_DEF(JSMSG_BAD_OPERAND, 130, 1, JSEXN_SYNTAXERR, "invalid {0} operand") MSG_DEF(JSMSG_BAD_PROP_ID, 131, 0, JSEXN_SYNTAXERR, "invalid property id") MSG_DEF(JSMSG_RESERVED_ID, 132, 1, JSEXN_SYNTAXERR, "{0} is a reserved identifier") MSG_DEF(JSMSG_SYNTAX_ERROR, 133, 0, JSEXN_SYNTAXERR, "syntax error") MSG_DEF(JSMSG_BAD_SHARP_VAR_DEF, 134, 0, JSEXN_SYNTAXERR, "invalid sharp variable definition") MSG_DEF(JSMSG_BAD_PROTOTYPE, 135, 1, JSEXN_TYPEERR, "'prototype' property of {0} is not an object") MSG_DEF(JSMSG_MISSING_EXPONENT, 136, 0, JSEXN_SYNTAXERR, "missing exponent") MSG_DEF(JSMSG_OUT_OF_MEMORY, 137, 0, JSEXN_ERR, "out of memory") MSG_DEF(JSMSG_UNTERMINATED_STRING, 138, 0, JSEXN_SYNTAXERR, "unterminated string literal") MSG_DEF(JSMSG_TOO_MANY_PARENS, 139, 0, JSEXN_INTERNALERR, "too many parentheses in regular expression") MSG_DEF(JSMSG_UNTERMINATED_COMMENT, 140, 0, JSEXN_SYNTAXERR, "unterminated comment") MSG_DEF(JSMSG_UNTERMINATED_REGEXP, 141, 0, JSEXN_SYNTAXERR, "unterminated regular expression literal") MSG_DEF(JSMSG_BAD_REGEXP_FLAG, 142, 0, JSEXN_SYNTAXERR, "invalid flag after regular expression") MSG_DEF(JSMSG_SHARPVAR_TOO_BIG, 143, 0, JSEXN_SYNTAXERR, "overlarge sharp variable number") MSG_DEF(JSMSG_ILLEGAL_CHARACTER, 144, 0, JSEXN_SYNTAXERR, "illegal character") MSG_DEF(JSMSG_BAD_OCTAL, 145, 1, JSEXN_SYNTAXERR, "{0} is not a legal ECMA-262 octal constant") MSG_DEF(JSMSG_BAD_INDIRECT_CALL, 146, 1, JSEXN_EVALERR, "function {0} must be called directly, and not by way of a function of another name") MSG_DEF(JSMSG_UNCAUGHT_EXCEPTION, 147, 1, JSEXN_INTERNALERR, "uncaught exception: {0}") MSG_DEF(JSMSG_INVALID_BACKREF, 148, 0, JSEXN_SYNTAXERR, "non-octal digit in an escape sequence that doesn't match a back-reference") MSG_DEF(JSMSG_BAD_BACKREF, 149, 0, JSEXN_SYNTAXERR, "back-reference exceeds number of capturing parentheses") MSG_DEF(JSMSG_PRECISION_RANGE, 150, 1, JSEXN_RANGEERR, "precision {0} out of range") MSG_DEF(JSMSG_BAD_GETTER_OR_SETTER, 151, 1, JSEXN_SYNTAXERR, "invalid {0} usage") MSG_DEF(JSMSG_BAD_ARRAY_LENGTH, 152, 0, JSEXN_RANGEERR, "invalid array length") MSG_DEF(JSMSG_CANT_DESCRIBE_PROPS, 153, 1, JSEXN_TYPEERR, "can't describe non-native properties of class {0}") MSG_DEF(JSMSG_BAD_APPLY_ARGS, 154, 1, JSEXN_TYPEERR, "second argument to Function.prototype.{0} must be an array") MSG_DEF(JSMSG_REDECLARED_VAR, 155, 2, JSEXN_TYPEERR, "redeclaration of {0} {1}") MSG_DEF(JSMSG_UNDECLARED_VAR, 156, 1, JSEXN_TYPEERR, "assignment to undeclared variable {0}") MSG_DEF(JSMSG_ANON_NO_RETURN_VALUE, 157, 0, JSEXN_TYPEERR, "anonymous function does not always return a value") MSG_DEF(JSMSG_DEPRECATED_USAGE, 158, 1, JSEXN_REFERENCEERR, "deprecated {0} usage") MSG_DEF(JSMSG_BAD_URI, 159, 0, JSEXN_URIERR, "malformed URI sequence") MSG_DEF(JSMSG_GETTER_ONLY, 160, 0, JSEXN_TYPEERR, "setting a property that has only a getter") MSG_DEF(JSMSG_TRAILING_COMMA, 161, 0, JSEXN_SYNTAXERR, "trailing comma is not legal in ECMA-262 object initializers") MSG_DEF(JSMSG_UNDEFINED_PROP, 162, 1, JSEXN_REFERENCEERR, "reference to undefined property {0}") MSG_DEF(JSMSG_USELESS_EXPR, 163, 0, JSEXN_TYPEERR, "useless expression") MSG_DEF(JSMSG_REDECLARED_PARAM, 164, 1, JSEXN_TYPEERR, "redeclaration of formal parameter {0}") MSG_DEF(JSMSG_NEWREGEXP_FLAGGED, 165, 0, JSEXN_TYPEERR, "can't supply flags when constructing one RegExp from another") MSG_DEF(JSMSG_RESERVED_SLOT_RANGE, 166, 0, JSEXN_RANGEERR, "reserved slot index out of range") MSG_DEF(JSMSG_CANT_DECODE_PRINCIPALS, 167, 0, JSEXN_INTERNALERR, "can't decode JSPrincipals") MSG_DEF(JSMSG_CANT_SEAL_OBJECT, 168, 1, JSEXN_ERR, "can't seal {0} objects") MSG_DEF(JSMSG_TOO_MANY_CATCH_VARS, 169, 0, JSEXN_SYNTAXERR, "too many catch variables") MSG_DEF(JSMSG_BAD_XML_MARKUP, 170, 0, JSEXN_SYNTAXERR, "invalid XML markup") MSG_DEF(JSMSG_BAD_XML_CHARACTER, 171, 0, JSEXN_SYNTAXERR, "illegal XML character") MSG_DEF(JSMSG_BAD_DEFAULT_XML_NAMESPACE,172,0,JSEXN_SYNTAXERR, "invalid default XML namespace") MSG_DEF(JSMSG_BAD_XML_NAME_SYNTAX, 173, 0, JSEXN_SYNTAXERR, "invalid XML name") MSG_DEF(JSMSG_BRACKET_AFTER_ATTR_EXPR,174, 0, JSEXN_SYNTAXERR, "missing ] after attribute expression") MSG_DEF(JSMSG_NESTING_GENERATOR, 175, 1, JSEXN_TYPEERR, "already executing generator {0}") MSG_DEF(JSMSG_CURLY_IN_XML_EXPR, 176, 0, JSEXN_SYNTAXERR, "missing } in XML expression") MSG_DEF(JSMSG_BAD_XML_NAMESPACE, 177, 1, JSEXN_TYPEERR, "invalid XML namespace {0}") MSG_DEF(JSMSG_BAD_XML_ATTR_NAME, 178, 1, JSEXN_TYPEERR, "invalid XML attribute name {0}") MSG_DEF(JSMSG_BAD_XML_NAME, 179, 1, JSEXN_TYPEERR, "invalid XML name {0}") MSG_DEF(JSMSG_BAD_XML_CONVERSION, 180, 1, JSEXN_TYPEERR, "can't convert {0} to XML") MSG_DEF(JSMSG_BAD_XMLLIST_CONVERSION, 181, 1, JSEXN_TYPEERR, "can't convert {0} to XMLList") MSG_DEF(JSMSG_BAD_GENERATOR_SEND, 182, 1, JSEXN_TYPEERR, "attempt to send {0} to newborn generator") MSG_DEF(JSMSG_NO_ASSIGN_IN_XML_ATTR, 183, 0, JSEXN_SYNTAXERR, "missing = in XML attribute") MSG_DEF(JSMSG_BAD_XML_ATTR_VALUE, 184, 0, JSEXN_SYNTAXERR, "invalid XML attribute value") MSG_DEF(JSMSG_XML_TAG_NAME_MISMATCH, 185, 1, JSEXN_SYNTAXERR, "XML tag name mismatch (expected {0})") MSG_DEF(JSMSG_BAD_XML_TAG_SYNTAX, 186, 0, JSEXN_SYNTAXERR, "invalid XML tag syntax") MSG_DEF(JSMSG_BAD_XML_LIST_SYNTAX, 187, 0, JSEXN_SYNTAXERR, "invalid XML list syntax") MSG_DEF(JSMSG_INCOMPATIBLE_METHOD, 188, 3, JSEXN_TYPEERR, "{0} {1} called on incompatible {2}") MSG_DEF(JSMSG_CANT_SET_XML_ATTRS, 189, 0, JSEXN_INTERNALERR, "can't set XML property attributes") MSG_DEF(JSMSG_END_OF_XML_SOURCE, 190, 0, JSEXN_SYNTAXERR, "unexpected end of XML source") MSG_DEF(JSMSG_END_OF_XML_ENTITY, 191, 0, JSEXN_SYNTAXERR, "unexpected end of XML entity") MSG_DEF(JSMSG_BAD_XML_QNAME, 192, 0, JSEXN_SYNTAXERR, "invalid XML qualified name") MSG_DEF(JSMSG_BAD_FOR_EACH_LOOP, 193, 0, JSEXN_SYNTAXERR, "invalid for each loop") MSG_DEF(JSMSG_BAD_XMLLIST_PUT, 194, 1, JSEXN_TYPEERR, "can't set property {0} in XMLList") MSG_DEF(JSMSG_UNKNOWN_XML_ENTITY, 195, 1, JSEXN_TYPEERR, "unknown XML entity {0}") MSG_DEF(JSMSG_BAD_XML_NCR, 196, 1, JSEXN_TYPEERR, "malformed XML character {0}") MSG_DEF(JSMSG_UNDEFINED_XML_NAME, 197, 1, JSEXN_REFERENCEERR, "reference to undefined XML name {0}") MSG_DEF(JSMSG_DUPLICATE_XML_ATTR, 198, 1, JSEXN_TYPEERR, "duplicate XML attribute {0}") MSG_DEF(JSMSG_TOO_MANY_FUN_VARS, 199, 0, JSEXN_SYNTAXERR, "too many local variables") MSG_DEF(JSMSG_ARRAY_INIT_TOO_BIG, 200, 0, JSEXN_INTERNALERR, "array initialiser too large") MSG_DEF(JSMSG_REGEXP_TOO_COMPLEX, 201, 0, JSEXN_INTERNALERR, "regular expression too complex") MSG_DEF(JSMSG_BUFFER_TOO_SMALL, 202, 0, JSEXN_INTERNALERR, "buffer too small") MSG_DEF(JSMSG_BAD_SURROGATE_CHAR, 203, 1, JSEXN_TYPEERR, "bad surrogate character {0}") MSG_DEF(JSMSG_UTF8_CHAR_TOO_LARGE, 204, 1, JSEXN_TYPEERR, "UTF-8 character {0} too large") MSG_DEF(JSMSG_MALFORMED_UTF8_CHAR, 205, 1, JSEXN_TYPEERR, "malformed UTF-8 character sequence at offset {0}") MSG_DEF(JSMSG_USER_DEFINED_ERROR, 206, 0, JSEXN_ERR, "JS_ReportError was called") MSG_DEF(JSMSG_WRONG_CONSTRUCTOR, 207, 1, JSEXN_TYPEERR, "wrong constructor called for {0}") MSG_DEF(JSMSG_BAD_GENERATOR_RETURN, 208, 1, JSEXN_TYPEERR, "generator function {0} returns a value") MSG_DEF(JSMSG_BAD_ANON_GENERATOR_RETURN, 209, 0, JSEXN_TYPEERR, "anonymous generator function returns a value") MSG_DEF(JSMSG_NAME_AFTER_FOR_PAREN, 210, 0, JSEXN_SYNTAXERR, "missing name after for (") MSG_DEF(JSMSG_IN_AFTER_FOR_NAME, 211, 0, JSEXN_SYNTAXERR, "missing in after for") MSG_DEF(JSMSG_BAD_ITERATOR_RETURN, 212, 2, JSEXN_TYPEERR, "{0}.{1} returned a primitive value") MSG_DEF(JSMSG_KEYWORD_NOT_NS, 213, 0, JSEXN_SYNTAXERR, "keyword is used as namespace") MSG_DEF(JSMSG_BAD_GENERATOR_YIELD, 214, 1, JSEXN_TYPEERR, "yield from closing generator {0}") MSG_DEF(JSMSG_BAD_YIELD_SYNTAX, 215, 0, JSEXN_SYNTAXERR, "yield expression must be parenthesized") MSG_DEF(JSMSG_ARRAY_COMP_LEFTSIDE, 216, 0, JSEXN_SYNTAXERR, "invalid array comprehension left-hand side") MSG_DEF(JSMSG_YIELD_FROM_FILTER, 217, 0, JSEXN_INTERNALERR, "yield not yet supported from filtering predicate") MSG_DEF(JSMSG_COMPILE_EXECED_SCRIPT, 218, 0, JSEXN_TYPEERR, "cannot compile over a script that is currently executing") MSG_DEF(JSMSG_NON_LIST_XML_METHOD, 219, 2, JSEXN_TYPEERR, "cannot call {0} method on an XML list with {1} elements") pacparser-1.4.5/src/spidermonkey/js/src/js.pkg000066400000000000000000000000601464010763600213260ustar00rootroot00000000000000[gecko xpi-bootstrap] dist/bin/@SHARED_LIBRARY@ pacparser-1.4.5/src/spidermonkey/js/src/js3240.rc000066400000000000000000000034251464010763600214720ustar00rootroot00000000000000//Microsoft Developer Studio generated resource script. // #include "resource.h" #define APSTUDIO_READONLY_SYMBOLS ///////////////////////////////////////////////////////////////////////////// // // Generated from the TEXTINCLUDE 2 resource. // #include "winver.h" ///////////////////////////////////////////////////////////////////////////// #undef APSTUDIO_READONLY_SYMBOLS ///////////////////////////////////////////////////////////////////////////// // // Version // VS_VERSION_INFO VERSIONINFO FILEVERSION 4,0,0,0 PRODUCTVERSION 4,0,0,0 FILEFLAGSMASK 0x3fL #ifdef _DEBUG FILEFLAGS 0x1L #else FILEFLAGS 0x0L #endif FILEOS 0x10004L FILETYPE 0x2L FILESUBTYPE 0x0L BEGIN BLOCK "StringFileInfo" BEGIN BLOCK "040904e4" BEGIN VALUE "CompanyName", "Netscape Communications Corporation\0" VALUE "FileDescription", "Netscape 32-bit JavaScript Module\0" VALUE "FileVersion", "4.0\0" VALUE "InternalName", "JS3240\0" VALUE "LegalCopyright", "Copyright Netscape Communications. 1994-96\0" VALUE "LegalTrademarks", "Netscape, Mozilla\0" VALUE "OriginalFilename", "js3240.dll\0" VALUE "ProductName", "NETSCAPE\0" VALUE "ProductVersion", "4.0\0" END END BLOCK "VarFileInfo" BEGIN VALUE "Translation", 0x409, 1252 END END #ifdef APSTUDIO_INVOKED ///////////////////////////////////////////////////////////////////////////// // // TEXTINCLUDE // 1 TEXTINCLUDE DISCARDABLE BEGIN "resource.h\0" END 2 TEXTINCLUDE DISCARDABLE BEGIN "#include ""winver.h""\r\n" "\0" END 3 TEXTINCLUDE DISCARDABLE BEGIN "\r\n" "\0" END #endif // APSTUDIO_INVOKED /////////////////////////////////////////////////////////////////////////////pacparser-1.4.5/src/spidermonkey/js/src/jsOS240.def000066400000000000000000000354621464010763600220110ustar00rootroot00000000000000; ***** BEGIN LICENSE BLOCK ***** ; Version: MPL 1.1/GPL 2.0/LGPL 2.1 ; ; The contents of this file are subject to the Mozilla Public License Version ; 1.1 (the "License"); you may not use this file except in compliance with ; the License. You may obtain a copy of the License at ; http://www.mozilla.org/MPL/ ; ; Software distributed under the License is distributed on an "AS IS" basis, ; WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License ; for the specific language governing rights and limitations under the ; License. ; ; The Original Code is Mozilla Communicator client code, released ; March 31, 1998. ; ; The Initial Developer of the Original Code is ; Netscape Communications Corporation. ; Portions created by the Initial Developer are Copyright (C) 1998 ; the Initial Developer. All Rights Reserved. ; ; Contributor(s): ; ; Alternatively, the contents of this file may be used under the terms of ; either of the GNU General Public License Version 2 or later (the "GPL"), ; or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), ; in which case the provisions of the GPL or the LGPL are applicable instead ; of those above. If you wish to allow use of your version of this file only ; under the terms of either the GPL or the LGPL, and not to allow others to ; use your version of this file under the terms of the MPL, indicate your ; decision by deleting the provisions above and replace them with the notice ; and other provisions required by the GPL or the LGPL. If you do not delete ; the provisions above, a recipient may use your version of this file under ; the terms of any one of the MPL, the GPL or the LGPL. ; ; ***** END LICENSE BLOCK ***** LIBRARY JS3240 INITINSTANCE TERMINSTANCE PROTMODE DESCRIPTION 'Netscape OS/2 JavaScript Library' CODE LOADONCALL MOVEABLE DISCARDABLE DATA PRELOAD MOVEABLE MULTIPLE NONSHARED EXPORTS ;====================== win16 exports these at least... =========== ; JS_Init = JS_Init @2 ; JS_Finish = JS_Finish @3 ; JS_GetNaNValue ; JS_GetNegativeInfinityValue ; JS_GetPositiveInfinityValue ; JS_GetEmptyStringValue ; JS_ConvertValue ; JS_ValueToObject ; JS_ValueToFunction ; JS_ValueToString ; JS_ValueToNumber ; JS_ValueToBoolean ; JS_TypeOfValue ; JS_GetTypeName ; JS_Lock ; JS_Unlock ; JS_NewContext ; JS_DestroyContext ; JS_ContextIterator ; JS_GetGlobalObject ; JS_SetGlobalObject ; JS_InitStandardClasses ;; JS_GetStaticLink ; JS_malloc ; JS_realloc ; JS_free ; JS_strdup ; JS_NewDouble ; JS_NewDoubleValue ; JS_AddRoot ; JS_RemoveRoot ; JS_LockGCThing ; JS_UnlockGCThing ; JS_GC ; JS_PropertyStub ; JS_EnumerateStub ; JS_ResolveStub ; JS_ConvertStub ; JS_FinalizeStub ; JS_InitClass ; JS_GetClass ; JS_InstanceOf ; JS_GetPrivate ; JS_SetPrivate ; JS_GetInstancePrivate ; JS_GetPrototype ; JS_GetParent ; JS_SetParent ; JS_GetConstructor ; JS_NewObject ; JS_DefineObject ; JS_DefineConstDoubles ; JS_DefineProperties ; JS_DefineProperty ; JS_DefinePropertyWithTinyId ; JS_AliasProperty ; JS_LookupProperty ; JS_GetProperty ; JS_SetProperty ; JS_DeleteProperty ; JS_NewArrayObject ; JS_DefineElement ; JS_AliasElement ; JS_LookupElement ; JS_GetElement ; JS_SetElement ; JS_DeleteElement ; JS_ClearScope ; JS_NewFunction ; JS_GetFunctionObject ; JS_GetFunctionName ; JS_DefineFunctions ; JS_DefineFunction ; JS_CompileScript ; JS_DestroyScript ; JS_CompileFunction ; JS_DecompileScript ; JS_DecompileFunction ; JS_DecompileFunctionBody ; JS_ExecuteScript ; JS_EvaluateScript ; JS_CallFunction ; JS_CallFunctionName ; JS_CallFunctionValue ; JS_SetBranchCallback ; JS_IsRunning ; JS_IsConstructing ; JS_SetCallReturnValue2 ; JS_NewString ; JS_NewStringCopyN ; JS_NewStringCopyZ ; JS_InternString ; JS_GetStringBytes ; JS_GetStringLength ; JS_CompareStrings ; JS_ReportError ; JS_ReportOutOfMemory ; JS_SetErrorReporter ; JS_NewRegExpObject ; JS_SetRegExpInput ; JS_ClearRegExpStatics ;================================================= ;00001:jsstr (OFFSET:0x00002e17, SIZE:0x0000ae17): ; - Public Definitions: ; js_EmptySubString ; js_CompareStrings ; js_HashString ; js_ValueToString ; js_StringToObject ; js_FinalizeString ; js_NewStringCopyZ ; js_NewString ; js_InitStringClass ; js_NewStringCopyN ; js_BoyerMooreHorspool ; ; ;00002:jsscript (OFFSET:0x0000dc2e, SIZE:0x00003abb): ; - Public Definitions: ; js_LineNumberToPC ; js_PCToLineNumber ; js_GetSrcNote ; js_DestroyScript ; js_NewScript ; ; ;00003:jsscope (OFFSET:0x000116e9, SIZE:0x00004f82): ; - Public Definitions: ; js_hash_scope_ops ; js_list_scope_ops ; js_DestroyProperty ; js_NewProperty ; js_IdToValue ; js_HashValue ; js_DestroyScope ; js_MutateScope ; js_DropScope ; js_HoldScope ; js_NewScope ; js_GetMutableScope ; js_HoldProperty ; js_DropProperty ; ; ;00004:jsscan (OFFSET:0x0001666b, SIZE:0x00008890): ; - Public Definitions: ; js_MatchToken ; js_FlushNewlines ; js_PeekTokenSameLine ; js_UngetToken ; js_GetToken ; js_PeekToken ; js_ReportCompileError js_CloseTokenStream js_NewBufferTokenStream ; js_NewTokenStream ; js_InitScanner ; ; ;00005:jsregexp (OFFSET:0x0001eefb, SIZE:0x0000eee4): ; - Public Definitions: ; js_RegExpClass ; reopsize ; js_NewRegExpObject ; js_InitRegExpClass ; js_FreeRegExpStatics ; js_InitRegExpStatics ; js_ExecuteRegExp ; js_NewRegExpOpt ; js_DestroyRegExp ; js_NewRegExp ; ; ;00006:jsparse (OFFSET:0x0002dddf, SIZE:0x00010b71): ; - Public Definitions: ; js_ParseFunctionBody js_Parse ; ; ;00007:jsopcode (OFFSET:0x0003e950, SIZE:0x0000d362): ; - Public Definitions: ; js_EscapeMap ; js_NumCodeSpecs ; js_CodeSpec ; js_incop_str ; js_true_str ; js_false_str ; js_this_str ; js_null_str ; js_void_str ; js_typeof_str ; js_delete_str ; js_new_str ; js_ValueToSource ; js_DecompileScript ; js_DecompileCode ; js_DecompileFunction ; js_puts ; js_printf ; js_GetPrinterOutput ; js_DestroyPrinter ; js_NewPrinter ; js_EscapeString ; js_Disassemble1 ; js_Disassemble ; ;00008:jsobj (OFFSET:0x0004bcb2, SIZE:0x000090a4): ; - Public Definitions: ; js_WithClass ; js_ObjectClass ; js_TryValueOf ; js_ValueToNonNullObject ; js_TryMethod ; js_ObjectToString ; js_SetClassPrototype ; js_DeleteProperty2 ; js_DeleteProperty ; js_SetProperty ; js_GetProperty ; js_FindVariableScope ; js_FindVariable ; js_FindProperty ; js_LookupProperty ; js_DefineProperty ; js_FreeSlot ; js_AllocSlot ; js_FinalizeObject ; js_GetClassPrototype ; js_NewObject ; js_InitObjectClass ; js_ValueToObject ; js_obj_toString ; js_SetSlot ; js_GetSlot ; ; ;00009:jsnum (OFFSET:0x00054d56, SIZE:0x00004f29): ; - Public Definitions: ; js_ValueToInt32 ; js_NumberToObject ; js_FinalizeDouble ; js_InitNumberClass ; js_NumberToString ; js_NewDoubleValue ; js_NewDouble ; js_ValueToNumber ; ; ;00010:jsmath (OFFSET:0x00059c7f, SIZE:0x000054b6): ; - Public Definitions: ; js_InitMathClass ; ; ;00011:jsjava (OFFSET:0x0005f135, SIZE:0x00022aad): ; - Public Definitions: ; js_Hooks ; MojaSrcLog ; finalizeTask JSJ_FindCurrentJSContext ; JSJ_GetPrincipals JSJ_IsSafeMethod JSJ_InitContext JSJ_Init js_JSErrorToJException js_JavaErrorReporter js_RemoveReflection js_ReflectJObjectToJSObject js_convertJObjectToJSValue js_convertJSValueToJObject js_ReflectJSObjectToJObject ; js_ReflectJClassToJSObject JSJ_ExitJS JSJ_EnterJS JSJ_CurrentContext JSJ_IsEnabled ;added in GA code - DSR70297 JSJ_Finish JSJ_IsCalledFromJava js_GetJSPrincipalsFromJavaCaller ; ; ;00012:jsinterp (OFFSET:0x00081be2, SIZE:0x00012274): ; - Public Definitions: ; js_Call ; js_Interpret ; js_SetLocalVariable ; js_GetLocalVariable ; js_SetArgument ; js_GetArgument ; js_FlushPropertyCacheByProp ; js_FlushPropertyCache ; ; ;00013:jsgc (OFFSET:0x00093e56, SIZE:0x00004f8d): ; - Public Definitions: ; js_ForceGC ; js_UnlockGCThing ; js_LockGCThing ; js_GC ; js_AllocGCThing ; js_RemoveRoot ; js_AddRoot ; js_FinishGC ; js_InitGC ; ; ;00014:jsfun (OFFSET:0x00098de3, SIZE:0x0000977c): ; - Public Definitions: ; js_FunctionClass ; js_ClosureClass ; js_CallClass ; js_DefineFunction ; js_NewFunction ; js_InitCallAndClosureClasses ; js_InitFunctionClass ; js_ValueToFunction ; js_SetCallVariable ; js_GetCallVariable ; js_PutCallObject ; js_GetCallObject ; ; ;00015:jsemit (OFFSET:0x000a255f, SIZE:0x000077be): ; - Public Definitions: ; js_SrcNoteName ; js_SrcNoteArity js_FinishTakingSrcNotes ; js_MoveSrcNotes ; js_GetSrcNoteOffset ; js_BumpSrcNoteDelta ; js_NewSrcNote3 ; js_NewSrcNote2 ; js_PopStatement ; js_EmitContinue ; js_EmitBreak ; js_SetSrcNoteOffset ; js_NewSrcNote ; js_PushStatement ; js_MoveCode ; js_SetJumpOffset ; js_Emit3 ; js_Emit2 ; js_Emit1 ; js_UpdateDepth ; js_SrcNoteLength ; js_CancelLastOpcode js_InitCodeGenerator ; ; ;00016:jsdbgapi (OFFSET:0x000a9d1d, SIZE:0x000057db): ; - Public Definitions: ; js_watchpoint_list ; js_trap_list ; JS_SetAnnotationInFrame ; JS_GetAnnotationFromFrame ; JS_GetJSPrincipalArrayFromFrame ; JS_NextJSFrame ; JS_InitJSFrameIterator JS_LineNumberToPC JS_PCToLineNumber JS_ClearAllWatchPoints JS_ClearWatchPoint JS_SetWatchPoint JS_HandleTrap JS_ClearAllTraps JS_ClearScriptTraps JS_ClearTrap JS_GetTrapOpcode JS_SetTrap ;DSR070297 - added in GA code JS_FrameIterator JS_GetFrameAnnotation JS_GetFramePrincipalArray JS_GetFrameScript JS_GetScriptFilename JS_SetFrameAnnotation JS_GetFramePC JS_GetFunctionScript ; ; ;00017:jsdate (OFFSET:0x000af4f8, SIZE:0x00009a8e): ; - Public Definitions: js_DateGetSeconds js_DateGetMinutes js_DateGetHours js_DateGetDate js_DateGetMonth js_DateGetYear js_NewDateObject ; js_InitDateClass ; ; ;00018:jscntxt (OFFSET:0x000b8f86, SIZE:0x00003732): ; - Public Definitions: ; js_InterpreterHooks ; js_ReportIsNotDefined ; js_ReportErrorAgain ; js_ReportErrorVA ; js_ContextIterator ; js_DestroyContext ; js_NewContext ; js_SetInterpreterHooks ; ; ;00019:jsbool (OFFSET:0x000bc6b8, SIZE:0x00003375): ; - Public Definitions: ; js_BooleanToString ; js_BooleanToObject ; js_InitBooleanClass ; js_ValueToBoolean ; ; ;00020:jsatom (OFFSET:0x000bfa2d, SIZE:0x000058d0): ; - Public Definitions: ; js_valueOf_str ; js_toString_str ; js_length_str ; js_eval_str ; js_constructor_str ; js_class_prototype_str ; js_assign_str ; js_anonymous_str ; js_Object_str ; js_Array_str ; js_type_str ; js_DropUnmappedAtoms js_FreeAtomMap js_InitAtomMap ; js_GetAtom ; js_DropAtom ; js_IndexAtom ; js_ValueToStringAtom ; js_AtomizeString ; js_AtomizeDouble ; js_AtomizeInt ; js_AtomizeBoolean ; js_AtomizeObject ; js_HoldAtom ; js_MarkAtomState ; js_FreeAtomState ; js_Atomize ; js_InitAtomState ; ; ;00021:jsarray (OFFSET:0x000c52fd, SIZE:0x00007c86): ; - Public Definitions: ; js_ArrayClass ; js_SetArrayLength ; js_GetArrayLength ; js_InitArrayClass ; js_NewArrayObject ; PR_qsort ; ; ;00022:jsapi (OFFSET:0x000ccf83, SIZE:0x0000de8c): ; - Public Definitions: JS_ClearRegExpStatics JS_SetRegExpInput JS_NewRegExpObject JS_SetErrorReporter JS_CompareStrings JS_GetStringLength JS_GetStringBytes JS_InternString JS_NewStringCopyZ JS_NewStringCopyN JS_NewString JS_IsRunning JS_SetBranchCallback JS_CallFunctionValue JS_CallFunctionName JS_CallFunction JS_EvaluateScriptForPrincipals JS_EvaluateScript JS_ExecuteScript JS_DecompileFunctionBody JS_DecompileFunction JS_DecompileScript JS_CompileFunctionForPrincipals JS_CompileFunction JS_DestroyScript JS_CompileScriptForPrincipals JS_CompileScript JS_DefineFunction JS_GetFunctionName JS_GetFunctionObject JS_NewFunction JS_ClearScope JS_DeleteElement JS_SetElement JS_GetElement JS_LookupElement JS_AliasElement JS_DefineElement JS_SetArrayLength JS_GetArrayLength JS_NewArrayObject JS_DeleteProperty JS_SetProperty JS_GetProperty JS_LookupProperty JS_AliasProperty JS_DefinePropertyWithTinyId JS_DefineProperty JS_DefineConstDoubles JS_DefineObject JS_NewObject JS_GetConstructor JS_SetParent JS_GetParent JS_SetPrototype JS_GetPrototype JS_GetInstancePrivate JS_SetPrivate JS_GetPrivate JS_InstanceOf JS_GetClass JS_DefineFunctions JS_DefineProperties JS_InitClass JS_FinalizeStub JS_ConvertStub JS_ResolveStub JS_EnumerateStub JS_PropertyStub JS_GC JS_UnlockGCThing JS_LockGCThing JS_RemoveRoot JS_AddRoot JS_NewDoubleValue JS_NewDouble JS_strdup JS_free JS_realloc JS_ReportOutOfMemory JS_malloc JS_GetScopeChain JS_InitStandardClasses JS_SetGlobalObject JS_GetGlobalObject JS_SetVersion JS_GetVersion JS_ContextIterator JS_GetTaskState JS_DestroyContext JS_NewContext JS_Unlock JS_Lock JS_Finish JS_Init JS_GetTypeName JS_TypeOfValue JS_ValueToBoolean JS_ValueToInt32 JS_ValueToNumber JS_ValueToString JS_ValueToFunction JS_ValueToObject JS_ReportError JS_ConvertValue JS_GetEmptyStringValue JS_GetPositiveInfinityValue JS_GetNegativeInfinityValue JS_GetNaNValue ;DSR062897 - added for GA code JS_MaybeGC JS_GetScriptPrincipals JS_IsAssigning JS_SetCharSetInfo ;brendan@mozilla.org, 2-Sept-2000 JS_SetCallReturnValue2 JS_SetGCCallback JS_SetGCCallbackRT JS_AddExternalStringFinalizer JS_RemoveExternalStringFinalizer JS_NewExternalString ; ; ;00023:prmjtime (OFFSET:0x000dae0f, SIZE:0x00008986): ; - Public Definitions: PRMJ_FormatTimeUSEnglish PRMJ_gmtime PRMJ_FormatTime PRMJ_mktime PRMJ_ComputeTime PRMJ_localtime PRMJ_ExplodeTime PRMJ_ToLocal PRMJ_ToGMT PRMJ_NowLocal PRMJ_DSTOffset PRMJ_NowS PRMJ_NowMS PRMJ_Now PRMJ_ToExtendedTime PRMJ_ToBaseTime PRMJ_setDST PRMJ_LocalGMTDifference pacparser-1.4.5/src/spidermonkey/js/src/jsapi.c000066400000000000000000004224651464010763600215020ustar00rootroot00000000000000/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- * vim: set ts=8 sw=4 et tw=78: * * ***** BEGIN LICENSE BLOCK ***** * Version: MPL 1.1/GPL 2.0/LGPL 2.1 * * The contents of this file are subject to the Mozilla Public License Version * 1.1 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * http://www.mozilla.org/MPL/ * * Software distributed under the License is distributed on an "AS IS" basis, * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License * for the specific language governing rights and limitations under the * License. * * The Original Code is Mozilla Communicator client code, released * March 31, 1998. * * The Initial Developer of the Original Code is * Netscape Communications Corporation. * Portions created by the Initial Developer are Copyright (C) 1998 * the Initial Developer. All Rights Reserved. * * Contributor(s): * * Alternatively, the contents of this file may be used under the terms of * either of the GNU General Public License Version 2 or later (the "GPL"), * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), * in which case the provisions of the GPL or the LGPL are applicable instead * of those above. If you wish to allow use of your version of this file only * under the terms of either the GPL or the LGPL, and not to allow others to * use your version of this file under the terms of the MPL, indicate your * decision by deleting the provisions above and replace them with the notice * and other provisions required by the GPL or the LGPL. If you do not delete * the provisions above, a recipient may use your version of this file under * the terms of any one of the MPL, the GPL or the LGPL. * * ***** END LICENSE BLOCK ***** */ /* * JavaScript API. */ #include "jsstddef.h" #include #include #include #include #include "jstypes.h" #include "jsarena.h" /* Added by JSIFY */ #include "jsutil.h" /* Added by JSIFY */ #include "jsclist.h" #include "jsdhash.h" #include "jsprf.h" #include "jsapi.h" #include "jsarray.h" #include "jsatom.h" #include "jsbool.h" #include "jscntxt.h" #include "jsconfig.h" #include "jsdate.h" #include "jsdtoa.h" #include "jsemit.h" #include "jsexn.h" #include "jsfun.h" #include "jsgc.h" #include "jsinterp.h" #include "jslock.h" #include "jsmath.h" #include "jsnum.h" #include "jsobj.h" #include "jsopcode.h" #include "jsparse.h" #include "jsregexp.h" #include "jsscan.h" #include "jsscope.h" #include "jsscript.h" #include "jsstr.h" #include "prmjtime.h" #if JS_HAS_FILE_OBJECT #include "jsfile.h" #endif #if JS_HAS_XML_SUPPORT #include "jsxml.h" #endif #if JS_HAS_GENERATORS #include "jsiter.h" #endif #ifdef HAVE_VA_LIST_AS_ARRAY #define JS_ADDRESSOF_VA_LIST(ap) ((va_list *)(ap)) #else #define JS_ADDRESSOF_VA_LIST(ap) (&(ap)) #endif #if defined(JS_PARANOID_REQUEST) && defined(JS_THREADSAFE) #define CHECK_REQUEST(cx) JS_ASSERT(cx->requestDepth) #else #define CHECK_REQUEST(cx) ((void)0) #endif JS_PUBLIC_API(int64) JS_Now() { return PRMJ_Now(); } JS_PUBLIC_API(jsval) JS_GetNaNValue(JSContext *cx) { return DOUBLE_TO_JSVAL(cx->runtime->jsNaN); } JS_PUBLIC_API(jsval) JS_GetNegativeInfinityValue(JSContext *cx) { return DOUBLE_TO_JSVAL(cx->runtime->jsNegativeInfinity); } JS_PUBLIC_API(jsval) JS_GetPositiveInfinityValue(JSContext *cx) { return DOUBLE_TO_JSVAL(cx->runtime->jsPositiveInfinity); } JS_PUBLIC_API(jsval) JS_GetEmptyStringValue(JSContext *cx) { return STRING_TO_JSVAL(cx->runtime->emptyString); } static JSBool TryArgumentFormatter(JSContext *cx, const char **formatp, JSBool fromJS, jsval **vpp, va_list *app) { const char *format; JSArgumentFormatMap *map; format = *formatp; for (map = cx->argumentFormatMap; map; map = map->next) { if (!strncmp(format, map->format, map->length)) { *formatp = format + map->length; return map->formatter(cx, format, fromJS, vpp, app); } } JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_BAD_CHAR, format); return JS_FALSE; } JS_PUBLIC_API(JSBool) JS_ConvertArguments(JSContext *cx, uintN argc, jsval *argv, const char *format, ...) { va_list ap; JSBool ok; va_start(ap, format); ok = JS_ConvertArgumentsVA(cx, argc, argv, format, ap); va_end(ap); return ok; } JS_PUBLIC_API(JSBool) JS_ConvertArgumentsVA(JSContext *cx, uintN argc, jsval *argv, const char *format, va_list ap) { jsval *sp; JSBool required; char c; JSFunction *fun; jsdouble d; JSString *str; JSObject *obj; CHECK_REQUEST(cx); sp = argv; required = JS_TRUE; while ((c = *format++) != '\0') { if (isspace(c)) continue; if (c == '/') { required = JS_FALSE; continue; } if (sp == argv + argc) { if (required) { fun = js_ValueToFunction(cx, &argv[-2], 0); if (fun) { char numBuf[12]; JS_snprintf(numBuf, sizeof numBuf, "%u", argc); JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_MORE_ARGS_NEEDED, JS_GetFunctionName(fun), numBuf, (argc == 1) ? "" : "s"); } return JS_FALSE; } break; } switch (c) { case 'b': if (!js_ValueToBoolean(cx, *sp, va_arg(ap, JSBool *))) return JS_FALSE; break; case 'c': if (!js_ValueToUint16(cx, *sp, va_arg(ap, uint16 *))) return JS_FALSE; break; case 'i': if (!js_ValueToECMAInt32(cx, *sp, va_arg(ap, int32 *))) return JS_FALSE; break; case 'u': if (!js_ValueToECMAUint32(cx, *sp, va_arg(ap, uint32 *))) return JS_FALSE; break; case 'j': if (!js_ValueToInt32(cx, *sp, va_arg(ap, int32 *))) return JS_FALSE; break; case 'd': if (!js_ValueToNumber(cx, *sp, va_arg(ap, jsdouble *))) return JS_FALSE; break; case 'I': if (!js_ValueToNumber(cx, *sp, &d)) return JS_FALSE; *va_arg(ap, jsdouble *) = js_DoubleToInteger(d); break; case 's': case 'S': case 'W': str = js_ValueToString(cx, *sp); if (!str) return JS_FALSE; *sp = STRING_TO_JSVAL(str); if (c == 's') *va_arg(ap, char **) = JS_GetStringBytes(str); else if (c == 'W') *va_arg(ap, jschar **) = JS_GetStringChars(str); else *va_arg(ap, JSString **) = str; break; case 'o': if (!js_ValueToObject(cx, *sp, &obj)) return JS_FALSE; *sp = OBJECT_TO_JSVAL(obj); *va_arg(ap, JSObject **) = obj; break; case 'f': obj = js_ValueToFunctionObject(cx, sp, 0); if (!obj) return JS_FALSE; *va_arg(ap, JSFunction **) = (JSFunction *) JS_GetPrivate(cx, obj); break; case 'v': *va_arg(ap, jsval *) = *sp; break; case '*': break; default: format--; if (!TryArgumentFormatter(cx, &format, JS_TRUE, &sp, JS_ADDRESSOF_VA_LIST(ap))) { return JS_FALSE; } /* NB: the formatter already updated sp, so we continue here. */ continue; } sp++; } return JS_TRUE; } JS_PUBLIC_API(jsval *) JS_PushArguments(JSContext *cx, void **markp, const char *format, ...) { va_list ap; jsval *argv; va_start(ap, format); argv = JS_PushArgumentsVA(cx, markp, format, ap); va_end(ap); return argv; } JS_PUBLIC_API(jsval *) JS_PushArgumentsVA(JSContext *cx, void **markp, const char *format, va_list ap) { uintN argc; jsval *argv, *sp; char c; const char *cp; JSString *str; JSFunction *fun; JSStackHeader *sh; CHECK_REQUEST(cx); *markp = NULL; argc = 0; for (cp = format; (c = *cp) != '\0'; cp++) { /* * Count non-space non-star characters as individual jsval arguments. * This may over-allocate stack, but we'll fix below. */ if (isspace(c) || c == '*') continue; argc++; } sp = js_AllocStack(cx, argc, markp); if (!sp) return NULL; argv = sp; while ((c = *format++) != '\0') { if (isspace(c) || c == '*') continue; switch (c) { case 'b': *sp = BOOLEAN_TO_JSVAL((JSBool) va_arg(ap, int)); break; case 'c': *sp = INT_TO_JSVAL((uint16) va_arg(ap, unsigned int)); break; case 'i': case 'j': if (!js_NewNumberValue(cx, (jsdouble) va_arg(ap, int32), sp)) goto bad; break; case 'u': if (!js_NewNumberValue(cx, (jsdouble) va_arg(ap, uint32), sp)) goto bad; break; case 'd': case 'I': if (!js_NewDoubleValue(cx, va_arg(ap, jsdouble), sp)) goto bad; break; case 's': str = JS_NewStringCopyZ(cx, va_arg(ap, char *)); if (!str) goto bad; *sp = STRING_TO_JSVAL(str); break; case 'W': str = JS_NewUCStringCopyZ(cx, va_arg(ap, jschar *)); if (!str) goto bad; *sp = STRING_TO_JSVAL(str); break; case 'S': str = va_arg(ap, JSString *); *sp = STRING_TO_JSVAL(str); break; case 'o': *sp = OBJECT_TO_JSVAL(va_arg(ap, JSObject *)); break; case 'f': fun = va_arg(ap, JSFunction *); *sp = fun ? OBJECT_TO_JSVAL(fun->object) : JSVAL_NULL; break; case 'v': *sp = va_arg(ap, jsval); break; default: format--; if (!TryArgumentFormatter(cx, &format, JS_FALSE, &sp, JS_ADDRESSOF_VA_LIST(ap))) { goto bad; } /* NB: the formatter already updated sp, so we continue here. */ continue; } sp++; } /* * We may have overallocated stack due to a multi-character format code * handled by a JSArgumentFormatter. Give back that stack space! */ JS_ASSERT(sp <= argv + argc); if (sp < argv + argc) { /* Return slots not pushed to the current stack arena. */ cx->stackPool.current->avail = (jsuword)sp; /* Reduce the count of slots the GC will scan in this stack segment. */ sh = cx->stackHeaders; JS_ASSERT(JS_STACK_SEGMENT(sh) + sh->nslots == argv + argc); sh->nslots -= argc - (sp - argv); } return argv; bad: js_FreeStack(cx, *markp); return NULL; } JS_PUBLIC_API(void) JS_PopArguments(JSContext *cx, void *mark) { CHECK_REQUEST(cx); js_FreeStack(cx, mark); } JS_PUBLIC_API(JSBool) JS_AddArgumentFormatter(JSContext *cx, const char *format, JSArgumentFormatter formatter) { size_t length; JSArgumentFormatMap **mpp, *map; length = strlen(format); mpp = &cx->argumentFormatMap; while ((map = *mpp) != NULL) { /* Insert before any shorter string to match before prefixes. */ if (map->length < length) break; if (map->length == length && !strcmp(map->format, format)) goto out; mpp = &map->next; } map = (JSArgumentFormatMap *) JS_malloc(cx, sizeof *map); if (!map) return JS_FALSE; map->format = format; map->length = length; map->next = *mpp; *mpp = map; out: map->formatter = formatter; return JS_TRUE; } JS_PUBLIC_API(void) JS_RemoveArgumentFormatter(JSContext *cx, const char *format) { size_t length; JSArgumentFormatMap **mpp, *map; length = strlen(format); mpp = &cx->argumentFormatMap; while ((map = *mpp) != NULL) { if (map->length == length && !strcmp(map->format, format)) { *mpp = map->next; JS_free(cx, map); return; } mpp = &map->next; } } JS_PUBLIC_API(JSBool) JS_ConvertValue(JSContext *cx, jsval v, JSType type, jsval *vp) { JSBool ok, b; JSObject *obj; JSString *str; jsdouble d, *dp; CHECK_REQUEST(cx); switch (type) { case JSTYPE_VOID: *vp = JSVAL_VOID; ok = JS_TRUE; break; case JSTYPE_OBJECT: ok = js_ValueToObject(cx, v, &obj); if (ok) *vp = OBJECT_TO_JSVAL(obj); break; case JSTYPE_FUNCTION: *vp = v; obj = js_ValueToFunctionObject(cx, vp, JSV2F_SEARCH_STACK); ok = (obj != NULL); break; case JSTYPE_STRING: str = js_ValueToString(cx, v); ok = (str != NULL); if (ok) *vp = STRING_TO_JSVAL(str); break; case JSTYPE_NUMBER: ok = js_ValueToNumber(cx, v, &d); if (ok) { dp = js_NewDouble(cx, d, 0); ok = (dp != NULL); if (ok) *vp = DOUBLE_TO_JSVAL(dp); } break; case JSTYPE_BOOLEAN: ok = js_ValueToBoolean(cx, v, &b); if (ok) *vp = BOOLEAN_TO_JSVAL(b); break; default: { char numBuf[12]; JS_snprintf(numBuf, sizeof numBuf, "%d", (int)type); JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_BAD_TYPE, numBuf); ok = JS_FALSE; break; } } return ok; } JS_PUBLIC_API(JSBool) JS_ValueToObject(JSContext *cx, jsval v, JSObject **objp) { CHECK_REQUEST(cx); return js_ValueToObject(cx, v, objp); } JS_PUBLIC_API(JSFunction *) JS_ValueToFunction(JSContext *cx, jsval v) { CHECK_REQUEST(cx); return js_ValueToFunction(cx, &v, JSV2F_SEARCH_STACK); } JS_PUBLIC_API(JSFunction *) JS_ValueToConstructor(JSContext *cx, jsval v) { CHECK_REQUEST(cx); return js_ValueToFunction(cx, &v, JSV2F_SEARCH_STACK); } JS_PUBLIC_API(JSString *) JS_ValueToString(JSContext *cx, jsval v) { CHECK_REQUEST(cx); return js_ValueToString(cx, v); } JS_PUBLIC_API(JSBool) JS_ValueToNumber(JSContext *cx, jsval v, jsdouble *dp) { CHECK_REQUEST(cx); return js_ValueToNumber(cx, v, dp); } JS_PUBLIC_API(JSBool) JS_ValueToECMAInt32(JSContext *cx, jsval v, int32 *ip) { CHECK_REQUEST(cx); return js_ValueToECMAInt32(cx, v, ip); } JS_PUBLIC_API(JSBool) JS_ValueToECMAUint32(JSContext *cx, jsval v, uint32 *ip) { CHECK_REQUEST(cx); return js_ValueToECMAUint32(cx, v, ip); } JS_PUBLIC_API(JSBool) JS_ValueToInt32(JSContext *cx, jsval v, int32 *ip) { CHECK_REQUEST(cx); return js_ValueToInt32(cx, v, ip); } JS_PUBLIC_API(JSBool) JS_ValueToUint16(JSContext *cx, jsval v, uint16 *ip) { CHECK_REQUEST(cx); return js_ValueToUint16(cx, v, ip); } JS_PUBLIC_API(JSBool) JS_ValueToBoolean(JSContext *cx, jsval v, JSBool *bp) { CHECK_REQUEST(cx); return js_ValueToBoolean(cx, v, bp); } JS_PUBLIC_API(JSType) JS_TypeOfValue(JSContext *cx, jsval v) { JSType type; JSObject *obj; JSObjectOps *ops; JSClass *clasp; CHECK_REQUEST(cx); if (JSVAL_IS_OBJECT(v)) { type = JSTYPE_OBJECT; /* XXXbe JSTYPE_NULL for JS2 */ obj = JSVAL_TO_OBJECT(v); if (obj) { ops = obj->map->ops; #if JS_HAS_XML_SUPPORT if (ops == &js_XMLObjectOps.base) { type = JSTYPE_XML; } else #endif { /* * ECMA 262, 11.4.3 says that any native object that implements * [[Call]] should be of type "function". Note that RegExp and * Script are both of type "function" for compatibility with * older SpiderMonkeys. */ clasp = OBJ_GET_CLASS(cx, obj); if ((ops == &js_ObjectOps) ? (clasp->call ? (clasp == &js_RegExpClass || clasp == &js_ScriptClass) : clasp == &js_FunctionClass) : ops->call != NULL) { type = JSTYPE_FUNCTION; } else { #ifdef NARCISSUS if (!OBJ_GET_PROPERTY(cx, obj, ATOM_TO_JSID(cx->runtime->atomState .callAtom), &v)) { JS_ClearPendingException(cx); } else if (VALUE_IS_FUNCTION(cx, v)) { type = JSTYPE_FUNCTION; } #endif } } } } else if (JSVAL_IS_NUMBER(v)) { type = JSTYPE_NUMBER; } else if (JSVAL_IS_STRING(v)) { type = JSTYPE_STRING; } else if (JSVAL_IS_BOOLEAN(v)) { type = JSTYPE_BOOLEAN; } else { type = JSTYPE_VOID; } return type; } JS_PUBLIC_API(const char *) JS_GetTypeName(JSContext *cx, JSType type) { if ((uintN)type >= (uintN)JSTYPE_LIMIT) return NULL; return js_type_strs[type]; } /************************************************************************/ JS_PUBLIC_API(JSRuntime *) JS_NewRuntime(uint32 maxbytes) { JSRuntime *rt; #ifdef DEBUG static JSBool didFirstChecks; if (!didFirstChecks) { /* * This code asserts that the numbers associated with the error names * in jsmsg.def are monotonically increasing. It uses values for the * error names enumerated in jscntxt.c. It's not a compile-time check * but it's better than nothing. */ int errorNumber = 0; #define MSG_DEF(name, number, count, exception, format) \ JS_ASSERT(name == errorNumber++); #include "js.msg" #undef MSG_DEF #define MSG_DEF(name, number, count, exception, format) \ JS_BEGIN_MACRO \ uintN numfmtspecs = 0; \ const char *fmt; \ for (fmt = format; *fmt != '\0'; fmt++) { \ if (*fmt == '{' && isdigit(fmt[1])) \ ++numfmtspecs; \ } \ JS_ASSERT(count == numfmtspecs); \ JS_END_MACRO; #include "js.msg" #undef MSG_DEF didFirstChecks = JS_TRUE; } #endif /* DEBUG */ rt = (JSRuntime *) malloc(sizeof(JSRuntime)); if (!rt) return NULL; /* Initialize infallibly first, so we can goto bad and JS_DestroyRuntime. */ memset(rt, 0, sizeof(JSRuntime)); JS_INIT_CLIST(&rt->contextList); JS_INIT_CLIST(&rt->trapList); JS_INIT_CLIST(&rt->watchPointList); if (!js_InitGC(rt, maxbytes)) goto bad; #ifdef JS_THREADSAFE if (PR_FAILURE == PR_NewThreadPrivateIndex(&rt->threadTPIndex, js_ThreadDestructorCB)) { goto bad; } rt->gcLock = JS_NEW_LOCK(); if (!rt->gcLock) goto bad; rt->gcDone = JS_NEW_CONDVAR(rt->gcLock); if (!rt->gcDone) goto bad; rt->requestDone = JS_NEW_CONDVAR(rt->gcLock); if (!rt->requestDone) goto bad; /* this is asymmetric with JS_ShutDown: */ if (!js_SetupLocks(8, 16)) goto bad; rt->rtLock = JS_NEW_LOCK(); if (!rt->rtLock) goto bad; rt->stateChange = JS_NEW_CONDVAR(rt->gcLock); if (!rt->stateChange) goto bad; rt->setSlotLock = JS_NEW_LOCK(); if (!rt->setSlotLock) goto bad; rt->setSlotDone = JS_NEW_CONDVAR(rt->setSlotLock); if (!rt->setSlotDone) goto bad; rt->scopeSharingDone = JS_NEW_CONDVAR(rt->gcLock); if (!rt->scopeSharingDone) goto bad; rt->scopeSharingTodo = NO_SCOPE_SHARING_TODO; #endif rt->propertyCache.empty = JS_TRUE; if (!js_InitPropertyTree(rt)) goto bad; return rt; bad: JS_DestroyRuntime(rt); return NULL; } JS_PUBLIC_API(void) JS_DestroyRuntime(JSRuntime *rt) { #ifdef DEBUG /* Don't hurt everyone in leaky ol' Mozilla with a fatal JS_ASSERT! */ if (!JS_CLIST_IS_EMPTY(&rt->contextList)) { JSContext *cx, *iter = NULL; uintN cxcount = 0; while ((cx = js_ContextIterator(rt, JS_TRUE, &iter)) != NULL) cxcount++; fprintf(stderr, "JS API usage error: %u contexts left in runtime upon JS_DestroyRuntime.\n", cxcount); } #endif js_FreeRuntimeScriptState(rt); js_FinishAtomState(&rt->atomState); js_FinishGC(rt); #ifdef JS_THREADSAFE if (rt->gcLock) JS_DESTROY_LOCK(rt->gcLock); if (rt->gcDone) JS_DESTROY_CONDVAR(rt->gcDone); if (rt->requestDone) JS_DESTROY_CONDVAR(rt->requestDone); if (rt->rtLock) JS_DESTROY_LOCK(rt->rtLock); if (rt->stateChange) JS_DESTROY_CONDVAR(rt->stateChange); if (rt->setSlotLock) JS_DESTROY_LOCK(rt->setSlotLock); if (rt->setSlotDone) JS_DESTROY_CONDVAR(rt->setSlotDone); if (rt->scopeSharingDone) JS_DESTROY_CONDVAR(rt->scopeSharingDone); #else GSN_CACHE_CLEAR(&rt->gsnCache); #endif js_FinishPropertyTree(rt); free(rt); } JS_PUBLIC_API(void) JS_ShutDown(void) { js_FinishDtoa(); #ifdef JS_THREADSAFE js_CleanupLocks(); #endif } JS_PUBLIC_API(void *) JS_GetRuntimePrivate(JSRuntime *rt) { return rt->data; } JS_PUBLIC_API(void) JS_SetRuntimePrivate(JSRuntime *rt, void *data) { rt->data = data; } #ifdef JS_THREADSAFE JS_PUBLIC_API(void) JS_BeginRequest(JSContext *cx) { JSRuntime *rt; JS_ASSERT(cx->thread->id == js_CurrentThreadId()); if (!cx->requestDepth) { /* Wait until the GC is finished. */ rt = cx->runtime; JS_LOCK_GC(rt); /* NB: we use cx->thread here, not js_GetCurrentThread(). */ if (rt->gcThread != cx->thread) { while (rt->gcLevel > 0) JS_AWAIT_GC_DONE(rt); } /* Indicate that a request is running. */ rt->requestCount++; cx->requestDepth = 1; JS_UNLOCK_GC(rt); return; } cx->requestDepth++; } JS_PUBLIC_API(void) JS_EndRequest(JSContext *cx) { JSRuntime *rt; JSScope *scope, **todop; uintN nshares; CHECK_REQUEST(cx); JS_ASSERT(cx->requestDepth > 0); if (cx->requestDepth == 1) { /* Lock before clearing to interlock with ClaimScope, in jslock.c. */ rt = cx->runtime; JS_LOCK_GC(rt); cx->requestDepth = 0; /* See whether cx has any single-threaded scopes to start sharing. */ todop = &rt->scopeSharingTodo; nshares = 0; while ((scope = *todop) != NO_SCOPE_SHARING_TODO) { if (scope->ownercx != cx) { todop = &scope->u.link; continue; } *todop = scope->u.link; scope->u.link = NULL; /* null u.link for sanity ASAP */ /* * If js_DropObjectMap returns null, we held the last ref to scope. * The waiting thread(s) must have been killed, after which the GC * collected the object that held this scope. Unlikely, because it * requires that the GC ran (e.g., from a branch callback) during * this request, but possible. */ if (js_DropObjectMap(cx, &scope->map, NULL)) { js_InitLock(&scope->lock); scope->u.count = 0; /* NULL may not pun as 0 */ js_FinishSharingScope(rt, scope); /* set ownercx = NULL */ nshares++; } } if (nshares) JS_NOTIFY_ALL_CONDVAR(rt->scopeSharingDone); /* Give the GC a chance to run if this was the last request running. */ JS_ASSERT(rt->requestCount > 0); rt->requestCount--; if (rt->requestCount == 0) JS_NOTIFY_REQUEST_DONE(rt); JS_UNLOCK_GC(rt); return; } cx->requestDepth--; } /* Yield to pending GC operations, regardless of request depth */ JS_PUBLIC_API(void) JS_YieldRequest(JSContext *cx) { JSRuntime *rt; JS_ASSERT(cx->thread); CHECK_REQUEST(cx); rt = cx->runtime; JS_LOCK_GC(rt); JS_ASSERT(rt->requestCount > 0); rt->requestCount--; if (rt->requestCount == 0) JS_NOTIFY_REQUEST_DONE(rt); JS_UNLOCK_GC(rt); /* XXXbe give the GC or another request calling it a chance to run here? Assumes FIFO scheduling */ JS_LOCK_GC(rt); if (rt->gcThread != cx->thread) { while (rt->gcLevel > 0) JS_AWAIT_GC_DONE(rt); } rt->requestCount++; JS_UNLOCK_GC(rt); } JS_PUBLIC_API(jsrefcount) JS_SuspendRequest(JSContext *cx) { jsrefcount saveDepth = cx->requestDepth; while (cx->requestDepth) JS_EndRequest(cx); return saveDepth; } JS_PUBLIC_API(void) JS_ResumeRequest(JSContext *cx, jsrefcount saveDepth) { JS_ASSERT(!cx->requestDepth); while (--saveDepth >= 0) JS_BeginRequest(cx); } #endif /* JS_THREADSAFE */ JS_PUBLIC_API(void) JS_Lock(JSRuntime *rt) { JS_LOCK_RUNTIME(rt); } JS_PUBLIC_API(void) JS_Unlock(JSRuntime *rt) { JS_UNLOCK_RUNTIME(rt); } JS_PUBLIC_API(JSContextCallback) JS_SetContextCallback(JSRuntime *rt, JSContextCallback cxCallback) { JSContextCallback old; old = rt->cxCallback; rt->cxCallback = cxCallback; return old; } JS_PUBLIC_API(JSContext *) JS_NewContext(JSRuntime *rt, size_t stackChunkSize) { return js_NewContext(rt, stackChunkSize); } JS_PUBLIC_API(void) JS_DestroyContext(JSContext *cx) { js_DestroyContext(cx, JSDCM_FORCE_GC); } JS_PUBLIC_API(void) JS_DestroyContextNoGC(JSContext *cx) { js_DestroyContext(cx, JSDCM_NO_GC); } JS_PUBLIC_API(void) JS_DestroyContextMaybeGC(JSContext *cx) { js_DestroyContext(cx, JSDCM_MAYBE_GC); } JS_PUBLIC_API(void *) JS_GetContextPrivate(JSContext *cx) { return cx->data; } JS_PUBLIC_API(void) JS_SetContextPrivate(JSContext *cx, void *data) { cx->data = data; } JS_PUBLIC_API(JSRuntime *) JS_GetRuntime(JSContext *cx) { return cx->runtime; } JS_PUBLIC_API(JSContext *) JS_ContextIterator(JSRuntime *rt, JSContext **iterp) { return js_ContextIterator(rt, JS_TRUE, iterp); } JS_PUBLIC_API(JSVersion) JS_GetVersion(JSContext *cx) { return cx->version & JSVERSION_MASK; } JS_PUBLIC_API(JSVersion) JS_SetVersion(JSContext *cx, JSVersion version) { JSVersion oldVersion; JS_ASSERT(version != JSVERSION_UNKNOWN); JS_ASSERT((version & ~JSVERSION_MASK) == 0); oldVersion = cx->version & JSVERSION_MASK; if (version == oldVersion) return oldVersion; /* We no longer support 1.4 or below. */ if (version != JSVERSION_DEFAULT && version <= JSVERSION_1_4) return oldVersion; cx->version = (cx->version & ~JSVERSION_MASK) | version; js_OnVersionChange(cx); return oldVersion; } static struct v2smap { JSVersion version; const char *string; } v2smap[] = { {JSVERSION_1_0, "1.0"}, {JSVERSION_1_1, "1.1"}, {JSVERSION_1_2, "1.2"}, {JSVERSION_1_3, "1.3"}, {JSVERSION_1_4, "1.4"}, {JSVERSION_ECMA_3, "ECMAv3"}, {JSVERSION_1_5, "1.5"}, {JSVERSION_1_6, "1.6"}, {JSVERSION_1_7, "1.7"}, {JSVERSION_DEFAULT, js_default_str}, {JSVERSION_UNKNOWN, NULL}, /* must be last, NULL is sentinel */ }; JS_PUBLIC_API(const char *) JS_VersionToString(JSVersion version) { int i; for (i = 0; v2smap[i].string; i++) if (v2smap[i].version == version) return v2smap[i].string; return "unknown"; } JS_PUBLIC_API(JSVersion) JS_StringToVersion(const char *string) { int i; for (i = 0; v2smap[i].string; i++) if (strcmp(v2smap[i].string, string) == 0) return v2smap[i].version; return JSVERSION_UNKNOWN; } JS_PUBLIC_API(uint32) JS_GetOptions(JSContext *cx) { return cx->options; } #define SYNC_OPTIONS_TO_VERSION(cx) \ JS_BEGIN_MACRO \ if ((cx)->options & JSOPTION_XML) \ (cx)->version |= JSVERSION_HAS_XML; \ else \ (cx)->version &= ~JSVERSION_HAS_XML; \ JS_END_MACRO JS_PUBLIC_API(uint32) JS_SetOptions(JSContext *cx, uint32 options) { uint32 oldopts = cx->options; cx->options = options; SYNC_OPTIONS_TO_VERSION(cx); return oldopts; } JS_PUBLIC_API(uint32) JS_ToggleOptions(JSContext *cx, uint32 options) { uint32 oldopts = cx->options; cx->options ^= options; SYNC_OPTIONS_TO_VERSION(cx); return oldopts; } JS_PUBLIC_API(const char *) JS_GetImplementationVersion(void) { return "JavaScript-C 1.7.0 2007-10-03"; } JS_PUBLIC_API(JSObject *) JS_GetGlobalObject(JSContext *cx) { return cx->globalObject; } JS_PUBLIC_API(void) JS_SetGlobalObject(JSContext *cx, JSObject *obj) { cx->globalObject = obj; #if JS_HAS_XML_SUPPORT cx->xmlSettingFlags = 0; #endif } JSObject * js_InitFunctionAndObjectClasses(JSContext *cx, JSObject *obj) { JSDHashTable *table; JSBool resolving; JSRuntime *rt; JSResolvingKey key; JSResolvingEntry *entry; JSObject *fun_proto, *obj_proto; /* If cx has no global object, use obj so prototypes can be found. */ if (!cx->globalObject) JS_SetGlobalObject(cx, obj); /* Record Function and Object in cx->resolvingTable, if we are resolving. */ table = cx->resolvingTable; resolving = (table && table->entryCount); rt = cx->runtime; key.obj = obj; if (resolving) { key.id = ATOM_TO_JSID(rt->atomState.classAtoms[JSProto_Function]); entry = (JSResolvingEntry *) JS_DHashTableOperate(table, &key, JS_DHASH_ADD); if (entry && entry->key.obj && (entry->flags & JSRESFLAG_LOOKUP)) { /* Already resolving Function, record Object too. */ JS_ASSERT(entry->key.obj == obj); key.id = ATOM_TO_JSID(rt->atomState.classAtoms[JSProto_Object]); entry = (JSResolvingEntry *) JS_DHashTableOperate(table, &key, JS_DHASH_ADD); } if (!entry) { JS_ReportOutOfMemory(cx); return NULL; } JS_ASSERT(!entry->key.obj && entry->flags == 0); entry->key = key; entry->flags = JSRESFLAG_LOOKUP; } else { key.id = ATOM_TO_JSID(rt->atomState.classAtoms[JSProto_Object]); if (!js_StartResolving(cx, &key, JSRESFLAG_LOOKUP, &entry)) return NULL; key.id = ATOM_TO_JSID(rt->atomState.classAtoms[JSProto_Function]); if (!js_StartResolving(cx, &key, JSRESFLAG_LOOKUP, &entry)) { key.id = ATOM_TO_JSID(rt->atomState.classAtoms[JSProto_Object]); JS_DHashTableOperate(table, &key, JS_DHASH_REMOVE); return NULL; } table = cx->resolvingTable; } /* Initialize the function class first so constructors can be made. */ fun_proto = js_InitFunctionClass(cx, obj); if (!fun_proto) goto out; /* Initialize the object class next so Object.prototype works. */ obj_proto = js_InitObjectClass(cx, obj); if (!obj_proto) { fun_proto = NULL; goto out; } /* Function.prototype and the global object delegate to Object.prototype. */ OBJ_SET_PROTO(cx, fun_proto, obj_proto); if (!OBJ_GET_PROTO(cx, obj)) OBJ_SET_PROTO(cx, obj, obj_proto); out: /* If resolving, remove the other entry (Object or Function) from table. */ JS_DHashTableOperate(table, &key, JS_DHASH_REMOVE); if (!resolving) { /* If not resolving, remove the first entry added above, for Object. */ JS_ASSERT(key.id == \ ATOM_TO_JSID(rt->atomState.classAtoms[JSProto_Function])); key.id = ATOM_TO_JSID(rt->atomState.classAtoms[JSProto_Object]); JS_DHashTableOperate(table, &key, JS_DHASH_REMOVE); } return fun_proto; } JS_PUBLIC_API(JSBool) JS_InitStandardClasses(JSContext *cx, JSObject *obj) { JSAtom *atom; CHECK_REQUEST(cx); /* Define a top-level property 'undefined' with the undefined value. */ atom = cx->runtime->atomState.typeAtoms[JSTYPE_VOID]; if (!OBJ_DEFINE_PROPERTY(cx, obj, ATOM_TO_JSID(atom), JSVAL_VOID, NULL, NULL, JSPROP_PERMANENT, NULL)) { return JS_FALSE; } /* Function and Object require cooperative bootstrapping magic. */ if (!js_InitFunctionAndObjectClasses(cx, obj)) return JS_FALSE; /* Initialize the rest of the standard objects and functions. */ return js_InitArrayClass(cx, obj) && js_InitBlockClass(cx, obj) && js_InitBooleanClass(cx, obj) && js_InitCallClass(cx, obj) && js_InitExceptionClasses(cx, obj) && js_InitMathClass(cx, obj) && js_InitNumberClass(cx, obj) && js_InitRegExpClass(cx, obj) && js_InitStringClass(cx, obj) && #if JS_HAS_SCRIPT_OBJECT js_InitScriptClass(cx, obj) && #endif #if JS_HAS_XML_SUPPORT js_InitXMLClasses(cx, obj) && #endif #if JS_HAS_FILE_OBJECT js_InitFileClass(cx, obj) && #endif #if JS_HAS_GENERATORS js_InitIteratorClasses(cx, obj) && #endif js_InitDateClass(cx, obj); } #define ATOM_OFFSET(name) offsetof(JSAtomState,name##Atom) #define CLASS_ATOM_OFFSET(name) offsetof(JSAtomState,classAtoms[JSProto_##name]) #define OFFSET_TO_ATOM(rt,off) (*(JSAtom **)((char*)&(rt)->atomState + (off))) #define CLASP(name) (JSClass *)&js_##name##Class #define EAGER_ATOM(name) ATOM_OFFSET(name), NULL #define EAGER_CLASS_ATOM(name) CLASS_ATOM_OFFSET(name), NULL #define EAGER_ATOM_AND_CLASP(name) EAGER_CLASS_ATOM(name), CLASP(name) #define LAZY_ATOM(name) ATOM_OFFSET(lazy.name), js_##name##_str typedef struct JSStdName { JSObjectOp init; size_t atomOffset; /* offset of atom pointer in JSAtomState */ const char *name; /* null if atom is pre-pinned, else name */ JSClass *clasp; } JSStdName; static JSAtom * StdNameToAtom(JSContext *cx, JSStdName *stdn) { size_t offset; JSAtom *atom; const char *name; offset = stdn->atomOffset; atom = OFFSET_TO_ATOM(cx->runtime, offset); if (!atom) { name = stdn->name; if (name) { atom = js_Atomize(cx, name, strlen(name), ATOM_PINNED); OFFSET_TO_ATOM(cx->runtime, offset) = atom; } } return atom; } /* * Table of class initializers and their atom offsets in rt->atomState. * If you add a "standard" class, remember to update this table. */ static JSStdName standard_class_atoms[] = { {js_InitFunctionAndObjectClasses, EAGER_ATOM_AND_CLASP(Function)}, {js_InitFunctionAndObjectClasses, EAGER_ATOM_AND_CLASP(Object)}, {js_InitArrayClass, EAGER_ATOM_AND_CLASP(Array)}, {js_InitBlockClass, EAGER_ATOM_AND_CLASP(Block)}, {js_InitBooleanClass, EAGER_ATOM_AND_CLASP(Boolean)}, {js_InitDateClass, EAGER_ATOM_AND_CLASP(Date)}, {js_InitMathClass, EAGER_ATOM_AND_CLASP(Math)}, {js_InitNumberClass, EAGER_ATOM_AND_CLASP(Number)}, {js_InitStringClass, EAGER_ATOM_AND_CLASP(String)}, {js_InitCallClass, EAGER_ATOM_AND_CLASP(Call)}, {js_InitExceptionClasses, EAGER_ATOM_AND_CLASP(Error)}, {js_InitRegExpClass, EAGER_ATOM_AND_CLASP(RegExp)}, #if JS_HAS_SCRIPT_OBJECT {js_InitScriptClass, EAGER_ATOM_AND_CLASP(Script)}, #endif #if JS_HAS_XML_SUPPORT {js_InitXMLClass, EAGER_ATOM_AND_CLASP(XML)}, {js_InitNamespaceClass, EAGER_ATOM_AND_CLASP(Namespace)}, {js_InitQNameClass, EAGER_ATOM_AND_CLASP(QName)}, #endif #if JS_HAS_FILE_OBJECT {js_InitFileClass, EAGER_ATOM_AND_CLASP(File)}, #endif #if JS_HAS_GENERATORS {js_InitIteratorClasses, EAGER_ATOM_AND_CLASP(StopIteration)}, #endif {NULL, 0, NULL, NULL} }; /* * Table of top-level function and constant names and their init functions. * If you add a "standard" global function or property, remember to update * this table. */ static JSStdName standard_class_names[] = { /* ECMA requires that eval be a direct property of the global object. */ {js_InitObjectClass, EAGER_ATOM(eval), NULL}, /* Global properties and functions defined by the Number class. */ {js_InitNumberClass, LAZY_ATOM(NaN), NULL}, {js_InitNumberClass, LAZY_ATOM(Infinity), NULL}, {js_InitNumberClass, LAZY_ATOM(isNaN), NULL}, {js_InitNumberClass, LAZY_ATOM(isFinite), NULL}, {js_InitNumberClass, LAZY_ATOM(parseFloat), NULL}, {js_InitNumberClass, LAZY_ATOM(parseInt), NULL}, /* String global functions. */ {js_InitStringClass, LAZY_ATOM(escape), NULL}, {js_InitStringClass, LAZY_ATOM(unescape), NULL}, {js_InitStringClass, LAZY_ATOM(decodeURI), NULL}, {js_InitStringClass, LAZY_ATOM(encodeURI), NULL}, {js_InitStringClass, LAZY_ATOM(decodeURIComponent), NULL}, {js_InitStringClass, LAZY_ATOM(encodeURIComponent), NULL}, #if JS_HAS_UNEVAL {js_InitStringClass, LAZY_ATOM(uneval), NULL}, #endif /* Exception constructors. */ {js_InitExceptionClasses, EAGER_CLASS_ATOM(Error), CLASP(Error)}, {js_InitExceptionClasses, EAGER_CLASS_ATOM(InternalError), CLASP(Error)}, {js_InitExceptionClasses, EAGER_CLASS_ATOM(EvalError), CLASP(Error)}, {js_InitExceptionClasses, EAGER_CLASS_ATOM(RangeError), CLASP(Error)}, {js_InitExceptionClasses, EAGER_CLASS_ATOM(ReferenceError), CLASP(Error)}, {js_InitExceptionClasses, EAGER_CLASS_ATOM(SyntaxError), CLASP(Error)}, {js_InitExceptionClasses, EAGER_CLASS_ATOM(TypeError), CLASP(Error)}, {js_InitExceptionClasses, EAGER_CLASS_ATOM(URIError), CLASP(Error)}, #if JS_HAS_XML_SUPPORT {js_InitAnyNameClass, EAGER_ATOM_AND_CLASP(AnyName)}, {js_InitAttributeNameClass, EAGER_ATOM_AND_CLASP(AttributeName)}, {js_InitXMLClass, LAZY_ATOM(XMLList), &js_XMLClass}, {js_InitXMLClass, LAZY_ATOM(isXMLName), NULL}, #endif #if JS_HAS_GENERATORS {js_InitIteratorClasses, EAGER_ATOM_AND_CLASP(Iterator)}, {js_InitIteratorClasses, EAGER_ATOM_AND_CLASP(Generator)}, #endif {NULL, 0, NULL, NULL} }; static JSStdName object_prototype_names[] = { /* Object.prototype properties (global delegates to Object.prototype). */ {js_InitObjectClass, EAGER_ATOM(proto), NULL}, {js_InitObjectClass, EAGER_ATOM(parent), NULL}, {js_InitObjectClass, EAGER_ATOM(count), NULL}, #if JS_HAS_TOSOURCE {js_InitObjectClass, EAGER_ATOM(toSource), NULL}, #endif {js_InitObjectClass, EAGER_ATOM(toString), NULL}, {js_InitObjectClass, EAGER_ATOM(toLocaleString), NULL}, {js_InitObjectClass, EAGER_ATOM(valueOf), NULL}, #if JS_HAS_OBJ_WATCHPOINT {js_InitObjectClass, LAZY_ATOM(watch), NULL}, {js_InitObjectClass, LAZY_ATOM(unwatch), NULL}, #endif {js_InitObjectClass, LAZY_ATOM(hasOwnProperty), NULL}, {js_InitObjectClass, LAZY_ATOM(isPrototypeOf), NULL}, {js_InitObjectClass, LAZY_ATOM(propertyIsEnumerable), NULL}, #if JS_HAS_GETTER_SETTER {js_InitObjectClass, LAZY_ATOM(defineGetter), NULL}, {js_InitObjectClass, LAZY_ATOM(defineSetter), NULL}, {js_InitObjectClass, LAZY_ATOM(lookupGetter), NULL}, {js_InitObjectClass, LAZY_ATOM(lookupSetter), NULL}, #endif {NULL, 0, NULL, NULL} }; JS_PUBLIC_API(JSBool) JS_ResolveStandardClass(JSContext *cx, JSObject *obj, jsval id, JSBool *resolved) { JSString *idstr; JSRuntime *rt; JSAtom *atom; JSStdName *stdnm; uintN i; CHECK_REQUEST(cx); *resolved = JS_FALSE; if (!JSVAL_IS_STRING(id)) return JS_TRUE; idstr = JSVAL_TO_STRING(id); rt = cx->runtime; /* Check whether we're resolving 'undefined', and define it if so. */ atom = rt->atomState.typeAtoms[JSTYPE_VOID]; if (idstr == ATOM_TO_STRING(atom)) { *resolved = JS_TRUE; return OBJ_DEFINE_PROPERTY(cx, obj, ATOM_TO_JSID(atom), JSVAL_VOID, NULL, NULL, JSPROP_PERMANENT, NULL); } /* Try for class constructors/prototypes named by well-known atoms. */ stdnm = NULL; for (i = 0; standard_class_atoms[i].init; i++) { atom = OFFSET_TO_ATOM(rt, standard_class_atoms[i].atomOffset); if (idstr == ATOM_TO_STRING(atom)) { stdnm = &standard_class_atoms[i]; break; } } if (!stdnm) { /* Try less frequently used top-level functions and constants. */ for (i = 0; standard_class_names[i].init; i++) { atom = StdNameToAtom(cx, &standard_class_names[i]); if (!atom) return JS_FALSE; if (idstr == ATOM_TO_STRING(atom)) { stdnm = &standard_class_names[i]; break; } } if (!stdnm && !OBJ_GET_PROTO(cx, obj)) { /* * Try even less frequently used names delegated from the global * object to Object.prototype, but only if the Object class hasn't * yet been initialized. */ for (i = 0; object_prototype_names[i].init; i++) { atom = StdNameToAtom(cx, &object_prototype_names[i]); if (!atom) return JS_FALSE; if (idstr == ATOM_TO_STRING(atom)) { stdnm = &standard_class_names[i]; break; } } } } if (stdnm) { /* * If this standard class is anonymous and obj advertises itself as a * global object (in order to reserve slots for standard class object * pointers), then we don't want to resolve by name. * * If inversely, either id does not name a class, or id does not name * an anonymous class, or the global does not reserve slots for class * objects, then we must call the init hook here. */ if (stdnm->clasp && (stdnm->clasp->flags & JSCLASS_IS_ANONYMOUS) && (OBJ_GET_CLASS(cx, obj)->flags & JSCLASS_IS_GLOBAL)) { return JS_TRUE; } if (!stdnm->init(cx, obj)) return JS_FALSE; *resolved = JS_TRUE; } return JS_TRUE; } static JSBool AlreadyHasOwnProperty(JSContext *cx, JSObject *obj, JSAtom *atom) { JSScopeProperty *sprop; JSScope *scope; JS_ASSERT(OBJ_IS_NATIVE(obj)); JS_LOCK_OBJ(cx, obj); scope = OBJ_SCOPE(obj); sprop = SCOPE_GET_PROPERTY(scope, ATOM_TO_JSID(atom)); JS_UNLOCK_SCOPE(cx, scope); return sprop != NULL; } JS_PUBLIC_API(JSBool) JS_EnumerateStandardClasses(JSContext *cx, JSObject *obj) { JSRuntime *rt; JSAtom *atom; uintN i; CHECK_REQUEST(cx); rt = cx->runtime; /* Check whether we need to bind 'undefined' and define it if so. */ atom = rt->atomState.typeAtoms[JSTYPE_VOID]; if (!AlreadyHasOwnProperty(cx, obj, atom) && !OBJ_DEFINE_PROPERTY(cx, obj, ATOM_TO_JSID(atom), JSVAL_VOID, NULL, NULL, JSPROP_PERMANENT, NULL)) { return JS_FALSE; } /* Initialize any classes that have not been resolved yet. */ for (i = 0; standard_class_atoms[i].init; i++) { atom = OFFSET_TO_ATOM(rt, standard_class_atoms[i].atomOffset); if (!AlreadyHasOwnProperty(cx, obj, atom) && !standard_class_atoms[i].init(cx, obj)) { return JS_FALSE; } } return JS_TRUE; } static JSIdArray * AddAtomToArray(JSContext *cx, JSAtom *atom, JSIdArray *ida, jsint *ip) { jsint i, length; i = *ip; length = ida->length; if (i >= length) { ida = js_SetIdArrayLength(cx, ida, JS_MAX(length * 2, 8)); if (!ida) return NULL; JS_ASSERT(i < ida->length); } ida->vector[i] = ATOM_TO_JSID(atom); *ip = i + 1; return ida; } static JSIdArray * EnumerateIfResolved(JSContext *cx, JSObject *obj, JSAtom *atom, JSIdArray *ida, jsint *ip, JSBool *foundp) { *foundp = AlreadyHasOwnProperty(cx, obj, atom); if (*foundp) ida = AddAtomToArray(cx, atom, ida, ip); return ida; } JS_PUBLIC_API(JSIdArray *) JS_EnumerateResolvedStandardClasses(JSContext *cx, JSObject *obj, JSIdArray *ida) { JSRuntime *rt; jsint i, j, k; JSAtom *atom; JSBool found; JSObjectOp init; CHECK_REQUEST(cx); rt = cx->runtime; if (ida) { i = ida->length; } else { ida = js_NewIdArray(cx, 8); if (!ida) return NULL; i = 0; } /* Check whether 'undefined' has been resolved and enumerate it if so. */ atom = rt->atomState.typeAtoms[JSTYPE_VOID]; ida = EnumerateIfResolved(cx, obj, atom, ida, &i, &found); if (!ida) return NULL; /* Enumerate only classes that *have* been resolved. */ for (j = 0; standard_class_atoms[j].init; j++) { atom = OFFSET_TO_ATOM(rt, standard_class_atoms[j].atomOffset); ida = EnumerateIfResolved(cx, obj, atom, ida, &i, &found); if (!ida) return NULL; if (found) { init = standard_class_atoms[j].init; for (k = 0; standard_class_names[k].init; k++) { if (standard_class_names[k].init == init) { atom = StdNameToAtom(cx, &standard_class_names[k]); ida = AddAtomToArray(cx, atom, ida, &i); if (!ida) return NULL; } } if (init == js_InitObjectClass) { for (k = 0; object_prototype_names[k].init; k++) { atom = StdNameToAtom(cx, &object_prototype_names[k]); ida = AddAtomToArray(cx, atom, ida, &i); if (!ida) return NULL; } } } } /* Trim to exact length via js_SetIdArrayLength. */ return js_SetIdArrayLength(cx, ida, i); } #undef ATOM_OFFSET #undef CLASS_ATOM_OFFSET #undef OFFSET_TO_ATOM #undef CLASP #undef EAGER_ATOM #undef EAGER_CLASS_ATOM #undef EAGER_ATOM_CLASP #undef LAZY_ATOM JS_PUBLIC_API(JSBool) JS_GetClassObject(JSContext *cx, JSObject *obj, JSProtoKey key, JSObject **objp) { CHECK_REQUEST(cx); return js_GetClassObject(cx, obj, key, objp); } JS_PUBLIC_API(JSObject *) JS_GetScopeChain(JSContext *cx) { JSStackFrame *fp; fp = cx->fp; if (!fp) { JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_INACTIVE); return NULL; } return js_GetScopeChain(cx, fp); } JS_PUBLIC_API(void *) JS_malloc(JSContext *cx, size_t nbytes) { void *p; JS_ASSERT(nbytes != 0); if (nbytes == 0) nbytes = 1; p = malloc(nbytes); if (!p) { JS_ReportOutOfMemory(cx); return NULL; } js_UpdateMallocCounter(cx, nbytes); return p; } JS_PUBLIC_API(void *) JS_realloc(JSContext *cx, void *p, size_t nbytes) { p = realloc(p, nbytes); if (!p) JS_ReportOutOfMemory(cx); return p; } JS_PUBLIC_API(void) JS_free(JSContext *cx, void *p) { if (p) free(p); } JS_PUBLIC_API(char *) JS_strdup(JSContext *cx, const char *s) { size_t n; void *p; n = strlen(s) + 1; p = JS_malloc(cx, n); if (!p) return NULL; return (char *)memcpy(p, s, n); } JS_PUBLIC_API(jsdouble *) JS_NewDouble(JSContext *cx, jsdouble d) { CHECK_REQUEST(cx); return js_NewDouble(cx, d, 0); } JS_PUBLIC_API(JSBool) JS_NewDoubleValue(JSContext *cx, jsdouble d, jsval *rval) { CHECK_REQUEST(cx); return js_NewDoubleValue(cx, d, rval); } JS_PUBLIC_API(JSBool) JS_NewNumberValue(JSContext *cx, jsdouble d, jsval *rval) { CHECK_REQUEST(cx); return js_NewNumberValue(cx, d, rval); } #undef JS_AddRoot JS_PUBLIC_API(JSBool) JS_AddRoot(JSContext *cx, void *rp) { CHECK_REQUEST(cx); return js_AddRoot(cx, rp, NULL); } JS_PUBLIC_API(JSBool) JS_AddNamedRootRT(JSRuntime *rt, void *rp, const char *name) { return js_AddRootRT(rt, rp, name); } JS_PUBLIC_API(JSBool) JS_RemoveRoot(JSContext *cx, void *rp) { CHECK_REQUEST(cx); return js_RemoveRoot(cx->runtime, rp); } JS_PUBLIC_API(JSBool) JS_RemoveRootRT(JSRuntime *rt, void *rp) { return js_RemoveRoot(rt, rp); } JS_PUBLIC_API(JSBool) JS_AddNamedRoot(JSContext *cx, void *rp, const char *name) { CHECK_REQUEST(cx); return js_AddRoot(cx, rp, name); } JS_PUBLIC_API(void) JS_ClearNewbornRoots(JSContext *cx) { JS_CLEAR_WEAK_ROOTS(&cx->weakRoots); } JS_PUBLIC_API(JSBool) JS_EnterLocalRootScope(JSContext *cx) { CHECK_REQUEST(cx); return js_EnterLocalRootScope(cx); } JS_PUBLIC_API(void) JS_LeaveLocalRootScope(JSContext *cx) { CHECK_REQUEST(cx); js_LeaveLocalRootScope(cx); } JS_PUBLIC_API(void) JS_LeaveLocalRootScopeWithResult(JSContext *cx, jsval rval) { CHECK_REQUEST(cx); js_LeaveLocalRootScopeWithResult(cx, rval); } JS_PUBLIC_API(void) JS_ForgetLocalRoot(JSContext *cx, void *thing) { CHECK_REQUEST(cx); js_ForgetLocalRoot(cx, (jsval) thing); } #ifdef DEBUG JS_PUBLIC_API(void) JS_DumpNamedRoots(JSRuntime *rt, void (*dump)(const char *name, void *rp, void *data), void *data) { js_DumpNamedRoots(rt, dump, data); } #endif /* DEBUG */ JS_PUBLIC_API(uint32) JS_MapGCRoots(JSRuntime *rt, JSGCRootMapFun map, void *data) { return js_MapGCRoots(rt, map, data); } JS_PUBLIC_API(JSBool) JS_LockGCThing(JSContext *cx, void *thing) { JSBool ok; CHECK_REQUEST(cx); ok = js_LockGCThing(cx, thing); if (!ok) JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_CANT_LOCK); return ok; } JS_PUBLIC_API(JSBool) JS_LockGCThingRT(JSRuntime *rt, void *thing) { return js_LockGCThingRT(rt, thing); } JS_PUBLIC_API(JSBool) JS_UnlockGCThing(JSContext *cx, void *thing) { JSBool ok; CHECK_REQUEST(cx); ok = js_UnlockGCThingRT(cx->runtime, thing); if (!ok) JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_CANT_UNLOCK); return ok; } JS_PUBLIC_API(JSBool) JS_UnlockGCThingRT(JSRuntime *rt, void *thing) { return js_UnlockGCThingRT(rt, thing); } JS_PUBLIC_API(void) JS_MarkGCThing(JSContext *cx, void *thing, const char *name, void *arg) { JS_ASSERT(cx->runtime->gcLevel > 0); #ifdef JS_THREADSAFE JS_ASSERT(cx->runtime->gcThread->id == js_CurrentThreadId()); #endif GC_MARK(cx, thing, name); } JS_PUBLIC_API(void) JS_GC(JSContext *cx) { #if JS_HAS_GENERATORS /* Run previously scheduled but delayed close hooks. */ js_RunCloseHooks(cx); #endif /* Don't nuke active arenas if executing or compiling. */ if (cx->stackPool.current == &cx->stackPool.first) JS_FinishArenaPool(&cx->stackPool); if (cx->tempPool.current == &cx->tempPool.first) JS_FinishArenaPool(&cx->tempPool); js_GC(cx, GC_NORMAL); #if JS_HAS_GENERATORS /* * Run close hooks for objects that became unreachable after the last GC. */ js_RunCloseHooks(cx); #endif } JS_PUBLIC_API(void) JS_MaybeGC(JSContext *cx) { #ifdef WAY_TOO_MUCH_GC JS_GC(cx); #else JSRuntime *rt; uint32 bytes, lastBytes; rt = cx->runtime; bytes = rt->gcBytes; lastBytes = rt->gcLastBytes; /* * We run the GC if we used all available free GC cells and had to * allocate extra 1/5 of GC arenas since the last run of GC, or if * we have malloc'd more bytes through JS_malloc than we were told * to allocate by JS_NewRuntime. * * The reason for * bytes > 6/5 lastBytes * condition is the following. Bug 312238 changed bytes and lastBytes * to mean the total amount of memory that the GC uses now and right * after the last GC. * * Before the bug the variables meant the size of allocated GC things * now and right after the last GC. That size did not include the * memory taken by free GC cells and the condition was * bytes > 3/2 lastBytes. * That is, we run the GC if we have half again as many bytes of * GC-things as the last time we GC'd. To be compatible we need to * express that condition through the new meaning of bytes and * lastBytes. * * We write the original condition as * B*(1-F) > 3/2 Bl*(1-Fl) * where B is the total memory size allocated by GC and F is the free * cell density currently and Sl and Fl are the size and the density * right after GC. The density by definition is memory taken by free * cells divided by total amount of memory. In other words, B and Bl * are bytes and lastBytes with the new meaning and B*(1-F) and * Bl*(1-Fl) are bytes and lastBytes with the original meaning. * * Our task is to exclude F and Fl from the last statement. According * the stats from bug 331770 Fl is about 20-30% for GC allocations * that contribute to S and Sl for a typical run of the browser. It * means that the original condition implied that we did not run GC * unless we exhausted the pool of free cells. Indeed if we still * have free cells, then B == Bl since we did not yet allocated any * new arenas and the condition means * 1 - F > 3/2 (1-Fl) or 3/2Fl > 1/2 + F * That implies 3/2 Fl > 1/2 or Fl > 1/3. That can not be fulfilled * for the state described by the stats. So we can write the original * condition as: * F == 0 && B > 3/2 Bl(1-Fl) * Again using the stats we see that Fl is about 20% when the browser * starts up and when we are far from hitting rt->gcMaxBytes. With * this F we have * F == 0 && B > 3/2 Bl(1-0.8) or just B > 6/5 Bl. */ if ((bytes > 8192 && bytes > lastBytes + lastBytes / 5) || rt->gcMallocBytes >= rt->gcMaxMallocBytes) { JS_GC(cx); } #if JS_HAS_GENERATORS else { /* Run scheduled but not yet executed close hooks. */ js_RunCloseHooks(cx); } #endif #endif } JS_PUBLIC_API(JSGCCallback) JS_SetGCCallback(JSContext *cx, JSGCCallback cb) { return JS_SetGCCallbackRT(cx->runtime, cb); } JS_PUBLIC_API(JSGCCallback) JS_SetGCCallbackRT(JSRuntime *rt, JSGCCallback cb) { JSGCCallback oldcb; oldcb = rt->gcCallback; rt->gcCallback = cb; return oldcb; } JS_PUBLIC_API(JSBool) JS_IsAboutToBeFinalized(JSContext *cx, void *thing) { JS_ASSERT(thing); return js_IsAboutToBeFinalized(cx, thing); } JS_PUBLIC_API(void) JS_SetGCParameter(JSRuntime *rt, JSGCParamKey key, uint32 value) { switch (key) { case JSGC_MAX_BYTES: rt->gcMaxBytes = value; break; case JSGC_MAX_MALLOC_BYTES: rt->gcMaxMallocBytes = value; break; } } JS_PUBLIC_API(intN) JS_AddExternalStringFinalizer(JSStringFinalizeOp finalizer) { return js_ChangeExternalStringFinalizer(NULL, finalizer); } JS_PUBLIC_API(intN) JS_RemoveExternalStringFinalizer(JSStringFinalizeOp finalizer) { return js_ChangeExternalStringFinalizer(finalizer, NULL); } JS_PUBLIC_API(JSString *) JS_NewExternalString(JSContext *cx, jschar *chars, size_t length, intN type) { JSString *str; CHECK_REQUEST(cx); JS_ASSERT(GCX_EXTERNAL_STRING <= type && type < (intN) GCX_NTYPES); str = (JSString *) js_NewGCThing(cx, (uintN) type, sizeof(JSString)); if (!str) return NULL; str->length = length; str->chars = chars; return str; } JS_PUBLIC_API(intN) JS_GetExternalStringGCType(JSRuntime *rt, JSString *str) { uint8 type = (uint8) (*js_GetGCThingFlags(str) & GCF_TYPEMASK); if (type >= GCX_EXTERNAL_STRING) return (intN)type; JS_ASSERT(type == GCX_STRING || type == GCX_MUTABLE_STRING); return -1; } JS_PUBLIC_API(void) JS_SetThreadStackLimit(JSContext *cx, jsuword limitAddr) { #if JS_STACK_GROWTH_DIRECTION > 0 if (limitAddr == 0) limitAddr = (jsuword)-1; #endif cx->stackLimit = limitAddr; } /************************************************************************/ JS_PUBLIC_API(void) JS_DestroyIdArray(JSContext *cx, JSIdArray *ida) { JS_free(cx, ida); } JS_PUBLIC_API(JSBool) JS_ValueToId(JSContext *cx, jsval v, jsid *idp) { JSAtom *atom; CHECK_REQUEST(cx); if (JSVAL_IS_INT(v)) { *idp = INT_JSVAL_TO_JSID(v); } else { #if JS_HAS_XML_SUPPORT if (JSVAL_IS_OBJECT(v)) { *idp = OBJECT_JSVAL_TO_JSID(v); return JS_TRUE; } #endif atom = js_ValueToStringAtom(cx, v); if (!atom) return JS_FALSE; *idp = ATOM_TO_JSID(atom); } return JS_TRUE; } JS_PUBLIC_API(JSBool) JS_IdToValue(JSContext *cx, jsid id, jsval *vp) { CHECK_REQUEST(cx); *vp = ID_TO_VALUE(id); return JS_TRUE; } JS_PUBLIC_API(JSBool) JS_PropertyStub(JSContext *cx, JSObject *obj, jsval id, jsval *vp) { return JS_TRUE; } JS_PUBLIC_API(JSBool) JS_EnumerateStub(JSContext *cx, JSObject *obj) { return JS_TRUE; } JS_PUBLIC_API(JSBool) JS_ResolveStub(JSContext *cx, JSObject *obj, jsval id) { return JS_TRUE; } JS_PUBLIC_API(JSBool) JS_ConvertStub(JSContext *cx, JSObject *obj, JSType type, jsval *vp) { return js_TryValueOf(cx, obj, type, vp); } JS_PUBLIC_API(void) JS_FinalizeStub(JSContext *cx, JSObject *obj) { } JS_PUBLIC_API(JSObject *) JS_InitClass(JSContext *cx, JSObject *obj, JSObject *parent_proto, JSClass *clasp, JSNative constructor, uintN nargs, JSPropertySpec *ps, JSFunctionSpec *fs, JSPropertySpec *static_ps, JSFunctionSpec *static_fs) { JSAtom *atom; JSProtoKey key; JSObject *proto, *ctor; JSTempValueRooter tvr; jsval cval, rval; JSBool named; JSFunction *fun; CHECK_REQUEST(cx); atom = js_Atomize(cx, clasp->name, strlen(clasp->name), 0); if (!atom) return NULL; /* * When initializing a standard class, if no parent_proto (grand-proto of * instances of the class, parent-proto of the class's prototype object) * is given, we must use Object.prototype if it is available. Otherwise, * we could look up the wrong binding for a class name in obj. Example: * * String = Array; * print("hi there".join); * * should print undefined, not Array.prototype.join. This is required by * ECMA-262, alas. It might have been better to make String readonly and * permanent in the global object, instead -- but that's too big a change * to swallow at this point. */ key = JSCLASS_CACHED_PROTO_KEY(clasp); if (key != JSProto_Null && !parent_proto && !js_GetClassPrototype(cx, obj, INT_TO_JSID(JSProto_Object), &parent_proto)) { return NULL; } /* Create a prototype object for this class. */ proto = js_NewObject(cx, clasp, parent_proto, obj); if (!proto) return NULL; /* After this point, control must exit via label bad or out. */ JS_PUSH_TEMP_ROOT_OBJECT(cx, proto, &tvr); if (!constructor) { /* * Lacking a constructor, name the prototype (e.g., Math) unless this * class (a) is anonymous, i.e. for internal use only; (b) the class * of obj (the global object) is has a reserved slot indexed by key; * and (c) key is not the null key. */ if ((clasp->flags & JSCLASS_IS_ANONYMOUS) && (OBJ_GET_CLASS(cx, obj)->flags & JSCLASS_IS_GLOBAL) && key != JSProto_Null) { named = JS_FALSE; } else { named = OBJ_DEFINE_PROPERTY(cx, obj, ATOM_TO_JSID(atom), OBJECT_TO_JSVAL(proto), NULL, NULL, (clasp->flags & JSCLASS_IS_ANONYMOUS) ? JSPROP_READONLY | JSPROP_PERMANENT : 0, NULL); if (!named) goto bad; } ctor = proto; } else { /* Define the constructor function in obj's scope. */ fun = js_DefineFunction(cx, obj, atom, constructor, nargs, 0); named = (fun != NULL); if (!fun) goto bad; /* * Remember the class this function is a constructor for so that * we know to create an object of this class when we call the * constructor. */ fun->clasp = clasp; /* * Optionally construct the prototype object, before the class has * been fully initialized. Allow the ctor to replace proto with a * different object, as is done for operator new -- and as at least * XML support requires. */ ctor = fun->object; if (clasp->flags & JSCLASS_CONSTRUCT_PROTOTYPE) { cval = OBJECT_TO_JSVAL(ctor); if (!js_InternalConstruct(cx, proto, cval, 0, NULL, &rval)) goto bad; if (!JSVAL_IS_PRIMITIVE(rval) && JSVAL_TO_OBJECT(rval) != proto) proto = JSVAL_TO_OBJECT(rval); } /* Connect constructor and prototype by named properties. */ if (!js_SetClassPrototype(cx, ctor, proto, JSPROP_READONLY | JSPROP_PERMANENT)) { goto bad; } /* Bootstrap Function.prototype (see also JS_InitStandardClasses). */ if (OBJ_GET_CLASS(cx, ctor) == clasp) { JS_ASSERT(!OBJ_GET_PROTO(cx, ctor)); OBJ_SET_PROTO(cx, ctor, proto); } } /* Add properties and methods to the prototype and the constructor. */ if ((ps && !JS_DefineProperties(cx, proto, ps)) || (fs && !JS_DefineFunctions(cx, proto, fs)) || (static_ps && !JS_DefineProperties(cx, ctor, static_ps)) || (static_fs && !JS_DefineFunctions(cx, ctor, static_fs))) { goto bad; } /* If this is a standard class, cache its prototype. */ if (key != JSProto_Null && !js_SetClassObject(cx, obj, key, ctor)) goto bad; out: JS_POP_TEMP_ROOT(cx, &tvr); return proto; bad: if (named) (void) OBJ_DELETE_PROPERTY(cx, obj, ATOM_TO_JSID(atom), &rval); proto = NULL; goto out; } #ifdef JS_THREADSAFE JS_PUBLIC_API(JSClass *) JS_GetClass(JSContext *cx, JSObject *obj) { return (JSClass *) JSVAL_TO_PRIVATE(GC_AWARE_GET_SLOT(cx, obj, JSSLOT_CLASS)); } #else JS_PUBLIC_API(JSClass *) JS_GetClass(JSObject *obj) { return LOCKED_OBJ_GET_CLASS(obj); } #endif JS_PUBLIC_API(JSBool) JS_InstanceOf(JSContext *cx, JSObject *obj, JSClass *clasp, jsval *argv) { JSFunction *fun; CHECK_REQUEST(cx); if (OBJ_GET_CLASS(cx, obj) == clasp) return JS_TRUE; if (argv) { fun = js_ValueToFunction(cx, &argv[-2], 0); if (fun) { JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_INCOMPATIBLE_PROTO, clasp->name, JS_GetFunctionName(fun), OBJ_GET_CLASS(cx, obj)->name); } } return JS_FALSE; } JS_PUBLIC_API(JSBool) JS_HasInstance(JSContext *cx, JSObject *obj, jsval v, JSBool *bp) { return js_HasInstance(cx, obj, v, bp); } JS_PUBLIC_API(void *) JS_GetPrivate(JSContext *cx, JSObject *obj) { jsval v; JS_ASSERT(OBJ_GET_CLASS(cx, obj)->flags & JSCLASS_HAS_PRIVATE); v = GC_AWARE_GET_SLOT(cx, obj, JSSLOT_PRIVATE); if (!JSVAL_IS_INT(v)) return NULL; return JSVAL_TO_PRIVATE(v); } JS_PUBLIC_API(JSBool) JS_SetPrivate(JSContext *cx, JSObject *obj, void *data) { JS_ASSERT(OBJ_GET_CLASS(cx, obj)->flags & JSCLASS_HAS_PRIVATE); OBJ_SET_SLOT(cx, obj, JSSLOT_PRIVATE, PRIVATE_TO_JSVAL(data)); return JS_TRUE; } JS_PUBLIC_API(void *) JS_GetInstancePrivate(JSContext *cx, JSObject *obj, JSClass *clasp, jsval *argv) { if (!JS_InstanceOf(cx, obj, clasp, argv)) return NULL; return JS_GetPrivate(cx, obj); } JS_PUBLIC_API(JSObject *) JS_GetPrototype(JSContext *cx, JSObject *obj) { JSObject *proto; CHECK_REQUEST(cx); proto = JSVAL_TO_OBJECT(GC_AWARE_GET_SLOT(cx, obj, JSSLOT_PROTO)); /* Beware ref to dead object (we may be called from obj's finalizer). */ return proto && proto->map ? proto : NULL; } JS_PUBLIC_API(JSBool) JS_SetPrototype(JSContext *cx, JSObject *obj, JSObject *proto) { CHECK_REQUEST(cx); if (obj->map->ops->setProto) return obj->map->ops->setProto(cx, obj, JSSLOT_PROTO, proto); OBJ_SET_SLOT(cx, obj, JSSLOT_PROTO, OBJECT_TO_JSVAL(proto)); return JS_TRUE; } JS_PUBLIC_API(JSObject *) JS_GetParent(JSContext *cx, JSObject *obj) { JSObject *parent; parent = JSVAL_TO_OBJECT(GC_AWARE_GET_SLOT(cx, obj, JSSLOT_PARENT)); /* Beware ref to dead object (we may be called from obj's finalizer). */ return parent && parent->map ? parent : NULL; } JS_PUBLIC_API(JSBool) JS_SetParent(JSContext *cx, JSObject *obj, JSObject *parent) { CHECK_REQUEST(cx); if (obj->map->ops->setParent) return obj->map->ops->setParent(cx, obj, JSSLOT_PARENT, parent); OBJ_SET_SLOT(cx, obj, JSSLOT_PARENT, OBJECT_TO_JSVAL(parent)); return JS_TRUE; } JS_PUBLIC_API(JSObject *) JS_GetConstructor(JSContext *cx, JSObject *proto) { jsval cval; CHECK_REQUEST(cx); if (!OBJ_GET_PROPERTY(cx, proto, ATOM_TO_JSID(cx->runtime->atomState.constructorAtom), &cval)) { return NULL; } if (!VALUE_IS_FUNCTION(cx, cval)) { JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_NO_CONSTRUCTOR, OBJ_GET_CLASS(cx, proto)->name); return NULL; } return JSVAL_TO_OBJECT(cval); } JS_PUBLIC_API(JSBool) JS_GetObjectId(JSContext *cx, JSObject *obj, jsid *idp) { JS_ASSERT(((jsid)obj & JSID_TAGMASK) == 0); *idp = OBJECT_TO_JSID(obj); return JS_TRUE; } JS_PUBLIC_API(JSObject *) JS_NewObject(JSContext *cx, JSClass *clasp, JSObject *proto, JSObject *parent) { CHECK_REQUEST(cx); if (!clasp) clasp = &js_ObjectClass; /* default class is Object */ return js_NewObject(cx, clasp, proto, parent); } JS_PUBLIC_API(JSBool) JS_SealObject(JSContext *cx, JSObject *obj, JSBool deep) { JSScope *scope; JSIdArray *ida; uint32 nslots; jsval v, *vp, *end; if (!OBJ_IS_NATIVE(obj)) { JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_CANT_SEAL_OBJECT, OBJ_GET_CLASS(cx, obj)->name); return JS_FALSE; } scope = OBJ_SCOPE(obj); #if defined JS_THREADSAFE && defined DEBUG /* Insist on scope being used exclusively by cx's thread. */ if (scope->ownercx != cx) { JS_LOCK_OBJ(cx, obj); JS_ASSERT(OBJ_SCOPE(obj) == scope); JS_ASSERT(scope->ownercx == cx); JS_UNLOCK_SCOPE(cx, scope); } #endif /* Nothing to do if obj's scope is already sealed. */ if (SCOPE_IS_SEALED(scope)) return JS_TRUE; /* XXX Enumerate lazy properties now, as they can't be added later. */ ida = JS_Enumerate(cx, obj); if (!ida) return JS_FALSE; JS_DestroyIdArray(cx, ida); /* Ensure that obj has its own, mutable scope, and seal that scope. */ JS_LOCK_OBJ(cx, obj); scope = js_GetMutableScope(cx, obj); if (scope) SCOPE_SET_SEALED(scope); JS_UNLOCK_OBJ(cx, obj); if (!scope) return JS_FALSE; /* If we are not sealing an entire object graph, we're done. */ if (!deep) return JS_TRUE; /* Walk obj->slots and if any value is a non-null object, seal it. */ nslots = JS_MIN(scope->map.freeslot, scope->map.nslots); for (vp = obj->slots, end = vp + nslots; vp < end; vp++) { v = *vp; if (JSVAL_IS_PRIMITIVE(v)) continue; if (!JS_SealObject(cx, JSVAL_TO_OBJECT(v), deep)) return JS_FALSE; } return JS_TRUE; } JS_PUBLIC_API(JSObject *) JS_ConstructObject(JSContext *cx, JSClass *clasp, JSObject *proto, JSObject *parent) { CHECK_REQUEST(cx); if (!clasp) clasp = &js_ObjectClass; /* default class is Object */ return js_ConstructObject(cx, clasp, proto, parent, 0, NULL); } JS_PUBLIC_API(JSObject *) JS_ConstructObjectWithArguments(JSContext *cx, JSClass *clasp, JSObject *proto, JSObject *parent, uintN argc, jsval *argv) { CHECK_REQUEST(cx); if (!clasp) clasp = &js_ObjectClass; /* default class is Object */ return js_ConstructObject(cx, clasp, proto, parent, argc, argv); } static JSBool DefineProperty(JSContext *cx, JSObject *obj, const char *name, jsval value, JSPropertyOp getter, JSPropertyOp setter, uintN attrs, uintN flags, intN tinyid) { jsid id; JSAtom *atom; if (attrs & JSPROP_INDEX) { id = INT_TO_JSID(JS_PTR_TO_INT32(name)); atom = NULL; attrs &= ~JSPROP_INDEX; } else { atom = js_Atomize(cx, name, strlen(name), 0); if (!atom) return JS_FALSE; id = ATOM_TO_JSID(atom); } if (flags != 0 && OBJ_IS_NATIVE(obj)) { return js_DefineNativeProperty(cx, obj, id, value, getter, setter, attrs, flags, tinyid, NULL); } return OBJ_DEFINE_PROPERTY(cx, obj, id, value, getter, setter, attrs, NULL); } #define AUTO_NAMELEN(s,n) (((n) == (size_t)-1) ? js_strlen(s) : (n)) static JSBool DefineUCProperty(JSContext *cx, JSObject *obj, const jschar *name, size_t namelen, jsval value, JSPropertyOp getter, JSPropertyOp setter, uintN attrs, uintN flags, intN tinyid) { JSAtom *atom; atom = js_AtomizeChars(cx, name, AUTO_NAMELEN(name, namelen), 0); if (!atom) return JS_FALSE; if (flags != 0 && OBJ_IS_NATIVE(obj)) { return js_DefineNativeProperty(cx, obj, ATOM_TO_JSID(atom), value, getter, setter, attrs, flags, tinyid, NULL); } return OBJ_DEFINE_PROPERTY(cx, obj, ATOM_TO_JSID(atom), value, getter, setter, attrs, NULL); } JS_PUBLIC_API(JSObject *) JS_DefineObject(JSContext *cx, JSObject *obj, const char *name, JSClass *clasp, JSObject *proto, uintN attrs) { JSObject *nobj; CHECK_REQUEST(cx); if (!clasp) clasp = &js_ObjectClass; /* default class is Object */ nobj = js_NewObject(cx, clasp, proto, obj); if (!nobj) return NULL; if (!DefineProperty(cx, obj, name, OBJECT_TO_JSVAL(nobj), NULL, NULL, attrs, 0, 0)) { cx->weakRoots.newborn[GCX_OBJECT] = NULL; return NULL; } return nobj; } JS_PUBLIC_API(JSBool) JS_DefineConstDoubles(JSContext *cx, JSObject *obj, JSConstDoubleSpec *cds) { JSBool ok; jsval value; uintN flags; CHECK_REQUEST(cx); for (ok = JS_TRUE; cds->name; cds++) { ok = js_NewNumberValue(cx, cds->dval, &value); if (!ok) break; flags = cds->flags; if (!flags) flags = JSPROP_READONLY | JSPROP_PERMANENT; ok = DefineProperty(cx, obj, cds->name, value, NULL, NULL, flags, 0, 0); if (!ok) break; } return ok; } JS_PUBLIC_API(JSBool) JS_DefineProperties(JSContext *cx, JSObject *obj, JSPropertySpec *ps) { JSBool ok; CHECK_REQUEST(cx); for (ok = JS_TRUE; ps->name; ps++) { ok = DefineProperty(cx, obj, ps->name, JSVAL_VOID, ps->getter, ps->setter, ps->flags, SPROP_HAS_SHORTID, ps->tinyid); if (!ok) break; } return ok; } JS_PUBLIC_API(JSBool) JS_DefineProperty(JSContext *cx, JSObject *obj, const char *name, jsval value, JSPropertyOp getter, JSPropertyOp setter, uintN attrs) { CHECK_REQUEST(cx); return DefineProperty(cx, obj, name, value, getter, setter, attrs, 0, 0); } JS_PUBLIC_API(JSBool) JS_DefinePropertyWithTinyId(JSContext *cx, JSObject *obj, const char *name, int8 tinyid, jsval value, JSPropertyOp getter, JSPropertyOp setter, uintN attrs) { CHECK_REQUEST(cx); return DefineProperty(cx, obj, name, value, getter, setter, attrs, SPROP_HAS_SHORTID, tinyid); } static JSBool LookupProperty(JSContext *cx, JSObject *obj, const char *name, JSObject **objp, JSProperty **propp) { JSAtom *atom; atom = js_Atomize(cx, name, strlen(name), 0); if (!atom) return JS_FALSE; return OBJ_LOOKUP_PROPERTY(cx, obj, ATOM_TO_JSID(atom), objp, propp); } static JSBool LookupUCProperty(JSContext *cx, JSObject *obj, const jschar *name, size_t namelen, JSObject **objp, JSProperty **propp) { JSAtom *atom; atom = js_AtomizeChars(cx, name, AUTO_NAMELEN(name, namelen), 0); if (!atom) return JS_FALSE; return OBJ_LOOKUP_PROPERTY(cx, obj, ATOM_TO_JSID(atom), objp, propp); } JS_PUBLIC_API(JSBool) JS_AliasProperty(JSContext *cx, JSObject *obj, const char *name, const char *alias) { JSObject *obj2; JSProperty *prop; JSAtom *atom; JSBool ok; JSScopeProperty *sprop; CHECK_REQUEST(cx); if (!LookupProperty(cx, obj, name, &obj2, &prop)) return JS_FALSE; if (!prop) { js_ReportIsNotDefined(cx, name); return JS_FALSE; } if (obj2 != obj || !OBJ_IS_NATIVE(obj)) { OBJ_DROP_PROPERTY(cx, obj2, prop); JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_CANT_ALIAS, alias, name, OBJ_GET_CLASS(cx, obj2)->name); return JS_FALSE; } atom = js_Atomize(cx, alias, strlen(alias), 0); if (!atom) { ok = JS_FALSE; } else { sprop = (JSScopeProperty *)prop; ok = (js_AddNativeProperty(cx, obj, ATOM_TO_JSID(atom), sprop->getter, sprop->setter, sprop->slot, sprop->attrs, sprop->flags | SPROP_IS_ALIAS, sprop->shortid) != NULL); } OBJ_DROP_PROPERTY(cx, obj, prop); return ok; } static jsval LookupResult(JSContext *cx, JSObject *obj, JSObject *obj2, JSProperty *prop) { JSScopeProperty *sprop; jsval rval; if (!prop) { /* XXX bad API: no way to tell "not defined" from "void value" */ return JSVAL_VOID; } if (OBJ_IS_NATIVE(obj2)) { /* Peek at the native property's slot value, without doing a Get. */ sprop = (JSScopeProperty *)prop; rval = SPROP_HAS_VALID_SLOT(sprop, OBJ_SCOPE(obj2)) ? LOCKED_OBJ_GET_SLOT(obj2, sprop->slot) : JSVAL_TRUE; } else { /* XXX bad API: no way to return "defined but value unknown" */ rval = JSVAL_TRUE; } OBJ_DROP_PROPERTY(cx, obj2, prop); return rval; } static JSBool GetPropertyAttributes(JSContext *cx, JSObject *obj, JSAtom *atom, uintN *attrsp, JSBool *foundp, JSPropertyOp *getterp, JSPropertyOp *setterp) { JSObject *obj2; JSProperty *prop; JSBool ok; if (!atom) return JS_FALSE; if (!OBJ_LOOKUP_PROPERTY(cx, obj, ATOM_TO_JSID(atom), &obj2, &prop)) return JS_FALSE; if (!prop || obj != obj2) { *attrsp = 0; *foundp = JS_FALSE; if (getterp) *getterp = NULL; if (setterp) *setterp = NULL; if (prop) OBJ_DROP_PROPERTY(cx, obj2, prop); return JS_TRUE; } *foundp = JS_TRUE; ok = OBJ_GET_ATTRIBUTES(cx, obj, ATOM_TO_JSID(atom), prop, attrsp); if (ok && OBJ_IS_NATIVE(obj)) { JSScopeProperty *sprop = (JSScopeProperty *) prop; if (getterp) *getterp = sprop->getter; if (setterp) *setterp = sprop->setter; } OBJ_DROP_PROPERTY(cx, obj, prop); return ok; } static JSBool SetPropertyAttributes(JSContext *cx, JSObject *obj, JSAtom *atom, uintN attrs, JSBool *foundp) { JSObject *obj2; JSProperty *prop; JSBool ok; if (!atom) return JS_FALSE; if (!OBJ_LOOKUP_PROPERTY(cx, obj, ATOM_TO_JSID(atom), &obj2, &prop)) return JS_FALSE; if (!prop || obj != obj2) { *foundp = JS_FALSE; if (prop) OBJ_DROP_PROPERTY(cx, obj2, prop); return JS_TRUE; } *foundp = JS_TRUE; ok = OBJ_SET_ATTRIBUTES(cx, obj, ATOM_TO_JSID(atom), prop, &attrs); OBJ_DROP_PROPERTY(cx, obj, prop); return ok; } JS_PUBLIC_API(JSBool) JS_GetPropertyAttributes(JSContext *cx, JSObject *obj, const char *name, uintN *attrsp, JSBool *foundp) { CHECK_REQUEST(cx); return GetPropertyAttributes(cx, obj, js_Atomize(cx, name, strlen(name), 0), attrsp, foundp, NULL, NULL); } JS_PUBLIC_API(JSBool) JS_GetPropertyAttrsGetterAndSetter(JSContext *cx, JSObject *obj, const char *name, uintN *attrsp, JSBool *foundp, JSPropertyOp *getterp, JSPropertyOp *setterp) { CHECK_REQUEST(cx); return GetPropertyAttributes(cx, obj, js_Atomize(cx, name, strlen(name), 0), attrsp, foundp, getterp, setterp); } JS_PUBLIC_API(JSBool) JS_SetPropertyAttributes(JSContext *cx, JSObject *obj, const char *name, uintN attrs, JSBool *foundp) { CHECK_REQUEST(cx); return SetPropertyAttributes(cx, obj, js_Atomize(cx, name, strlen(name), 0), attrs, foundp); } JS_PUBLIC_API(JSBool) JS_HasProperty(JSContext *cx, JSObject *obj, const char *name, JSBool *foundp) { JSBool ok; JSObject *obj2; JSProperty *prop; CHECK_REQUEST(cx); ok = LookupProperty(cx, obj, name, &obj2, &prop); if (ok) { *foundp = (prop != NULL); if (prop) OBJ_DROP_PROPERTY(cx, obj2, prop); } return ok; } JS_PUBLIC_API(JSBool) JS_LookupProperty(JSContext *cx, JSObject *obj, const char *name, jsval *vp) { JSBool ok; JSObject *obj2; JSProperty *prop; CHECK_REQUEST(cx); ok = LookupProperty(cx, obj, name, &obj2, &prop); if (ok) *vp = LookupResult(cx, obj, obj2, prop); return ok; } JS_PUBLIC_API(JSBool) JS_LookupPropertyWithFlags(JSContext *cx, JSObject *obj, const char *name, uintN flags, jsval *vp) { JSAtom *atom; JSBool ok; JSObject *obj2; JSProperty *prop; CHECK_REQUEST(cx); atom = js_Atomize(cx, name, strlen(name), 0); if (!atom) return JS_FALSE; ok = OBJ_IS_NATIVE(obj) ? js_LookupPropertyWithFlags(cx, obj, ATOM_TO_JSID(atom), flags, &obj2, &prop) : OBJ_LOOKUP_PROPERTY(cx, obj, ATOM_TO_JSID(atom), &obj2, &prop); if (ok) *vp = LookupResult(cx, obj, obj2, prop); return ok; } JS_PUBLIC_API(JSBool) JS_GetProperty(JSContext *cx, JSObject *obj, const char *name, jsval *vp) { JSAtom *atom; CHECK_REQUEST(cx); atom = js_Atomize(cx, name, strlen(name), 0); if (!atom) return JS_FALSE; return OBJ_GET_PROPERTY(cx, obj, ATOM_TO_JSID(atom), vp); } JS_PUBLIC_API(JSBool) JS_GetMethodById(JSContext *cx, JSObject *obj, jsid id, JSObject **objp, jsval *vp) { CHECK_REQUEST(cx); #if JS_HAS_XML_SUPPORT if (OBJECT_IS_XML(cx, obj)) { JSXMLObjectOps *ops; ops = (JSXMLObjectOps *) obj->map->ops; obj = ops->getMethod(cx, obj, id, vp); if (!obj) return JS_FALSE; } else #endif { if (!OBJ_GET_PROPERTY(cx, obj, id, vp)) return JS_FALSE; } *objp = obj; return JS_TRUE; } JS_PUBLIC_API(JSBool) JS_GetMethod(JSContext *cx, JSObject *obj, const char *name, JSObject **objp, jsval *vp) { JSAtom *atom; atom = js_Atomize(cx, name, strlen(name), 0); if (!atom) return JS_FALSE; return JS_GetMethodById(cx, obj, ATOM_TO_JSID(atom), objp, vp); } JS_PUBLIC_API(JSBool) JS_SetProperty(JSContext *cx, JSObject *obj, const char *name, jsval *vp) { JSAtom *atom; CHECK_REQUEST(cx); atom = js_Atomize(cx, name, strlen(name), 0); if (!atom) return JS_FALSE; return OBJ_SET_PROPERTY(cx, obj, ATOM_TO_JSID(atom), vp); } JS_PUBLIC_API(JSBool) JS_DeleteProperty(JSContext *cx, JSObject *obj, const char *name) { jsval junk; CHECK_REQUEST(cx); return JS_DeleteProperty2(cx, obj, name, &junk); } JS_PUBLIC_API(JSBool) JS_DeleteProperty2(JSContext *cx, JSObject *obj, const char *name, jsval *rval) { JSAtom *atom; CHECK_REQUEST(cx); atom = js_Atomize(cx, name, strlen(name), 0); if (!atom) return JS_FALSE; return OBJ_DELETE_PROPERTY(cx, obj, ATOM_TO_JSID(atom), rval); } JS_PUBLIC_API(JSBool) JS_DefineUCProperty(JSContext *cx, JSObject *obj, const jschar *name, size_t namelen, jsval value, JSPropertyOp getter, JSPropertyOp setter, uintN attrs) { CHECK_REQUEST(cx); return DefineUCProperty(cx, obj, name, namelen, value, getter, setter, attrs, 0, 0); } JS_PUBLIC_API(JSBool) JS_GetUCPropertyAttributes(JSContext *cx, JSObject *obj, const jschar *name, size_t namelen, uintN *attrsp, JSBool *foundp) { CHECK_REQUEST(cx); return GetPropertyAttributes(cx, obj, js_AtomizeChars(cx, name, AUTO_NAMELEN(name, namelen), 0), attrsp, foundp, NULL, NULL); } JS_PUBLIC_API(JSBool) JS_GetUCPropertyAttrsGetterAndSetter(JSContext *cx, JSObject *obj, const jschar *name, size_t namelen, uintN *attrsp, JSBool *foundp, JSPropertyOp *getterp, JSPropertyOp *setterp) { CHECK_REQUEST(cx); return GetPropertyAttributes(cx, obj, js_AtomizeChars(cx, name, AUTO_NAMELEN(name, namelen), 0), attrsp, foundp, getterp, setterp); } JS_PUBLIC_API(JSBool) JS_SetUCPropertyAttributes(JSContext *cx, JSObject *obj, const jschar *name, size_t namelen, uintN attrs, JSBool *foundp) { CHECK_REQUEST(cx); return SetPropertyAttributes(cx, obj, js_AtomizeChars(cx, name, AUTO_NAMELEN(name, namelen), 0), attrs, foundp); } JS_PUBLIC_API(JSBool) JS_DefineUCPropertyWithTinyId(JSContext *cx, JSObject *obj, const jschar *name, size_t namelen, int8 tinyid, jsval value, JSPropertyOp getter, JSPropertyOp setter, uintN attrs) { CHECK_REQUEST(cx); return DefineUCProperty(cx, obj, name, namelen, value, getter, setter, attrs, SPROP_HAS_SHORTID, tinyid); } JS_PUBLIC_API(JSBool) JS_HasUCProperty(JSContext *cx, JSObject *obj, const jschar *name, size_t namelen, JSBool *vp) { JSBool ok; JSObject *obj2; JSProperty *prop; CHECK_REQUEST(cx); ok = LookupUCProperty(cx, obj, name, namelen, &obj2, &prop); if (ok) { *vp = (prop != NULL); if (prop) OBJ_DROP_PROPERTY(cx, obj2, prop); } return ok; } JS_PUBLIC_API(JSBool) JS_LookupUCProperty(JSContext *cx, JSObject *obj, const jschar *name, size_t namelen, jsval *vp) { JSBool ok; JSObject *obj2; JSProperty *prop; CHECK_REQUEST(cx); ok = LookupUCProperty(cx, obj, name, namelen, &obj2, &prop); if (ok) *vp = LookupResult(cx, obj, obj2, prop); return ok; } JS_PUBLIC_API(JSBool) JS_GetUCProperty(JSContext *cx, JSObject *obj, const jschar *name, size_t namelen, jsval *vp) { JSAtom *atom; CHECK_REQUEST(cx); atom = js_AtomizeChars(cx, name, AUTO_NAMELEN(name, namelen), 0); if (!atom) return JS_FALSE; return OBJ_GET_PROPERTY(cx, obj, ATOM_TO_JSID(atom), vp); } JS_PUBLIC_API(JSBool) JS_SetUCProperty(JSContext *cx, JSObject *obj, const jschar *name, size_t namelen, jsval *vp) { JSAtom *atom; CHECK_REQUEST(cx); atom = js_AtomizeChars(cx, name, AUTO_NAMELEN(name, namelen), 0); if (!atom) return JS_FALSE; return OBJ_SET_PROPERTY(cx, obj, ATOM_TO_JSID(atom), vp); } JS_PUBLIC_API(JSBool) JS_DeleteUCProperty2(JSContext *cx, JSObject *obj, const jschar *name, size_t namelen, jsval *rval) { JSAtom *atom; CHECK_REQUEST(cx); atom = js_AtomizeChars(cx, name, AUTO_NAMELEN(name, namelen), 0); if (!atom) return JS_FALSE; return OBJ_DELETE_PROPERTY(cx, obj, ATOM_TO_JSID(atom), rval); } JS_PUBLIC_API(JSObject *) JS_NewArrayObject(JSContext *cx, jsint length, jsval *vector) { CHECK_REQUEST(cx); /* NB: jsuint cast does ToUint32. */ return js_NewArrayObject(cx, (jsuint)length, vector); } JS_PUBLIC_API(JSBool) JS_IsArrayObject(JSContext *cx, JSObject *obj) { return OBJ_GET_CLASS(cx, obj) == &js_ArrayClass; } JS_PUBLIC_API(JSBool) JS_GetArrayLength(JSContext *cx, JSObject *obj, jsuint *lengthp) { CHECK_REQUEST(cx); return js_GetLengthProperty(cx, obj, lengthp); } JS_PUBLIC_API(JSBool) JS_SetArrayLength(JSContext *cx, JSObject *obj, jsuint length) { CHECK_REQUEST(cx); return js_SetLengthProperty(cx, obj, length); } JS_PUBLIC_API(JSBool) JS_HasArrayLength(JSContext *cx, JSObject *obj, jsuint *lengthp) { CHECK_REQUEST(cx); return js_HasLengthProperty(cx, obj, lengthp); } JS_PUBLIC_API(JSBool) JS_DefineElement(JSContext *cx, JSObject *obj, jsint index, jsval value, JSPropertyOp getter, JSPropertyOp setter, uintN attrs) { CHECK_REQUEST(cx); return OBJ_DEFINE_PROPERTY(cx, obj, INT_TO_JSID(index), value, getter, setter, attrs, NULL); } JS_PUBLIC_API(JSBool) JS_AliasElement(JSContext *cx, JSObject *obj, const char *name, jsint alias) { JSObject *obj2; JSProperty *prop; JSScopeProperty *sprop; JSBool ok; CHECK_REQUEST(cx); if (!LookupProperty(cx, obj, name, &obj2, &prop)) return JS_FALSE; if (!prop) { js_ReportIsNotDefined(cx, name); return JS_FALSE; } if (obj2 != obj || !OBJ_IS_NATIVE(obj)) { char numBuf[12]; OBJ_DROP_PROPERTY(cx, obj2, prop); JS_snprintf(numBuf, sizeof numBuf, "%ld", (long)alias); JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_CANT_ALIAS, numBuf, name, OBJ_GET_CLASS(cx, obj2)->name); return JS_FALSE; } sprop = (JSScopeProperty *)prop; ok = (js_AddNativeProperty(cx, obj, INT_TO_JSID(alias), sprop->getter, sprop->setter, sprop->slot, sprop->attrs, sprop->flags | SPROP_IS_ALIAS, sprop->shortid) != NULL); OBJ_DROP_PROPERTY(cx, obj, prop); return ok; } JS_PUBLIC_API(JSBool) JS_HasElement(JSContext *cx, JSObject *obj, jsint index, JSBool *foundp) { JSBool ok; JSObject *obj2; JSProperty *prop; CHECK_REQUEST(cx); ok = OBJ_LOOKUP_PROPERTY(cx, obj, INT_TO_JSID(index), &obj2, &prop); if (ok) { *foundp = (prop != NULL); if (prop) OBJ_DROP_PROPERTY(cx, obj2, prop); } return ok; } JS_PUBLIC_API(JSBool) JS_LookupElement(JSContext *cx, JSObject *obj, jsint index, jsval *vp) { JSBool ok; JSObject *obj2; JSProperty *prop; CHECK_REQUEST(cx); ok = OBJ_LOOKUP_PROPERTY(cx, obj, INT_TO_JSID(index), &obj2, &prop); if (ok) *vp = LookupResult(cx, obj, obj2, prop); return ok; } JS_PUBLIC_API(JSBool) JS_GetElement(JSContext *cx, JSObject *obj, jsint index, jsval *vp) { CHECK_REQUEST(cx); return OBJ_GET_PROPERTY(cx, obj, INT_TO_JSID(index), vp); } JS_PUBLIC_API(JSBool) JS_SetElement(JSContext *cx, JSObject *obj, jsint index, jsval *vp) { CHECK_REQUEST(cx); return OBJ_SET_PROPERTY(cx, obj, INT_TO_JSID(index), vp); } JS_PUBLIC_API(JSBool) JS_DeleteElement(JSContext *cx, JSObject *obj, jsint index) { jsval junk; CHECK_REQUEST(cx); return JS_DeleteElement2(cx, obj, index, &junk); } JS_PUBLIC_API(JSBool) JS_DeleteElement2(JSContext *cx, JSObject *obj, jsint index, jsval *rval) { CHECK_REQUEST(cx); return OBJ_DELETE_PROPERTY(cx, obj, INT_TO_JSID(index), rval); } JS_PUBLIC_API(void) JS_ClearScope(JSContext *cx, JSObject *obj) { CHECK_REQUEST(cx); if (obj->map->ops->clear) obj->map->ops->clear(cx, obj); /* Clear cached class objects on the global object. */ if (JS_GET_CLASS(cx, obj)->flags & JSCLASS_IS_GLOBAL) { JSProtoKey key; for (key = JSProto_Null; key < JSProto_LIMIT; key++) JS_SetReservedSlot(cx, obj, key, JSVAL_VOID); } } JS_PUBLIC_API(JSIdArray *) JS_Enumerate(JSContext *cx, JSObject *obj) { jsint i, n; jsval iter_state, num_properties; jsid id; JSIdArray *ida; jsval *vector; CHECK_REQUEST(cx); ida = NULL; iter_state = JSVAL_NULL; /* Get the number of properties to enumerate. */ if (!OBJ_ENUMERATE(cx, obj, JSENUMERATE_INIT, &iter_state, &num_properties)) goto error; if (!JSVAL_IS_INT(num_properties)) { JS_ASSERT(0); goto error; } /* Grow as needed if we don't know the exact amount ahead of time. */ n = JSVAL_TO_INT(num_properties); if (n <= 0) n = 8; /* Create an array of jsids large enough to hold all the properties */ ida = js_NewIdArray(cx, n); if (!ida) goto error; i = 0; vector = &ida->vector[0]; for (;;) { if (!OBJ_ENUMERATE(cx, obj, JSENUMERATE_NEXT, &iter_state, &id)) goto error; /* No more jsid's to enumerate ? */ if (iter_state == JSVAL_NULL) break; if (i == ida->length) { ida = js_SetIdArrayLength(cx, ida, ida->length * 2); if (!ida) goto error; vector = &ida->vector[0]; } vector[i++] = id; } return js_SetIdArrayLength(cx, ida, i); error: if (iter_state != JSVAL_NULL) OBJ_ENUMERATE(cx, obj, JSENUMERATE_DESTROY, &iter_state, 0); if (ida) JS_DestroyIdArray(cx, ida); return NULL; } /* * XXX reverse iterator for properties, unreverse and meld with jsinterp.c's * prop_iterator_class somehow... * + preserve the OBJ_ENUMERATE API while optimizing the native object case * + native case here uses a JSScopeProperty *, but that iterates in reverse! * + so we make non-native match, by reverse-iterating after JS_Enumerating */ #define JSSLOT_ITER_INDEX (JSSLOT_PRIVATE + 1) #if JSSLOT_ITER_INDEX >= JS_INITIAL_NSLOTS # error "JSSLOT_ITER_INDEX botch!" #endif static void prop_iter_finalize(JSContext *cx, JSObject *obj) { jsval v; jsint i; JSIdArray *ida; v = GC_AWARE_GET_SLOT(cx, obj, JSSLOT_ITER_INDEX); if (JSVAL_IS_VOID(v)) return; i = JSVAL_TO_INT(v); if (i >= 0) { /* Non-native case: destroy the ida enumerated when obj was created. */ ida = (JSIdArray *) JS_GetPrivate(cx, obj); if (ida) JS_DestroyIdArray(cx, ida); } } static uint32 prop_iter_mark(JSContext *cx, JSObject *obj, void *arg) { jsval v; jsint i, n; JSScopeProperty *sprop; JSIdArray *ida; jsid id; v = GC_AWARE_GET_SLOT(cx, obj, JSSLOT_PRIVATE); JS_ASSERT(!JSVAL_IS_VOID(v)); i = JSVAL_TO_INT(OBJ_GET_SLOT(cx, obj, JSSLOT_ITER_INDEX)); if (i < 0) { /* Native case: just mark the next property to visit. */ sprop = (JSScopeProperty *) JSVAL_TO_PRIVATE(v); if (sprop) MARK_SCOPE_PROPERTY(cx, sprop); } else { /* Non-native case: mark each id in the JSIdArray private. */ ida = (JSIdArray *) JSVAL_TO_PRIVATE(v); for (i = 0, n = ida->length; i < n; i++) { id = ida->vector[i]; MARK_ID(cx, id); } } return 0; } static JSClass prop_iter_class = { "PropertyIterator", JSCLASS_HAS_PRIVATE | JSCLASS_HAS_RESERVED_SLOTS(1), JS_PropertyStub, JS_PropertyStub, JS_PropertyStub, JS_PropertyStub, JS_EnumerateStub, JS_ResolveStub, JS_ConvertStub, prop_iter_finalize, NULL, NULL, NULL, NULL, NULL, NULL, prop_iter_mark, NULL }; JS_PUBLIC_API(JSObject *) JS_NewPropertyIterator(JSContext *cx, JSObject *obj) { JSObject *iterobj; JSScope *scope; void *pdata; jsint index; JSIdArray *ida; CHECK_REQUEST(cx); iterobj = js_NewObject(cx, &prop_iter_class, NULL, obj); if (!iterobj) return NULL; if (OBJ_IS_NATIVE(obj)) { /* Native case: start with the last property in obj's own scope. */ scope = OBJ_SCOPE(obj); pdata = (scope->object == obj) ? scope->lastProp : NULL; index = -1; } else { JSTempValueRooter tvr; /* * Non-native case: enumerate a JSIdArray and keep it via private. * * Note: we have to make sure that we root obj around the call to * JS_Enumerate to protect against multiple allocations under it. */ JS_PUSH_SINGLE_TEMP_ROOT(cx, OBJECT_TO_JSVAL(iterobj), &tvr); ida = JS_Enumerate(cx, obj); JS_POP_TEMP_ROOT(cx, &tvr); if (!ida) goto bad; pdata = ida; index = ida->length; } /* iterobj can not escape to other threads here. */ iterobj->slots[JSSLOT_PRIVATE] = PRIVATE_TO_JSVAL(pdata); iterobj->slots[JSSLOT_ITER_INDEX] = INT_TO_JSVAL(index); return iterobj; bad: cx->weakRoots.newborn[GCX_OBJECT] = NULL; return NULL; } JS_PUBLIC_API(JSBool) JS_NextProperty(JSContext *cx, JSObject *iterobj, jsid *idp) { jsint i; JSObject *obj; JSScope *scope; JSScopeProperty *sprop; JSIdArray *ida; CHECK_REQUEST(cx); i = JSVAL_TO_INT(OBJ_GET_SLOT(cx, iterobj, JSSLOT_ITER_INDEX)); if (i < 0) { /* Native case: private data is a property tree node pointer. */ obj = OBJ_GET_PARENT(cx, iterobj); JS_ASSERT(OBJ_IS_NATIVE(obj)); scope = OBJ_SCOPE(obj); JS_ASSERT(scope->object == obj); sprop = (JSScopeProperty *) JS_GetPrivate(cx, iterobj); /* * If the next property mapped by scope in the property tree ancestor * line is not enumerable, or it's an alias, or one or more properties * were deleted from the "middle" of the scope-mapped ancestor line * and the next property was among those deleted, skip it and keep on * trying to find an enumerable property that is still in scope. */ while (sprop && (!(sprop->attrs & JSPROP_ENUMERATE) || (sprop->flags & SPROP_IS_ALIAS) || (SCOPE_HAD_MIDDLE_DELETE(scope) && !SCOPE_HAS_PROPERTY(scope, sprop)))) { sprop = sprop->parent; } if (!sprop) { *idp = JSVAL_VOID; } else { if (!JS_SetPrivate(cx, iterobj, sprop->parent)) return JS_FALSE; *idp = sprop->id; } } else { /* Non-native case: use the ida enumerated when iterobj was created. */ ida = (JSIdArray *) JS_GetPrivate(cx, iterobj); JS_ASSERT(i <= ida->length); if (i == 0) { *idp = JSVAL_VOID; } else { *idp = ida->vector[--i]; OBJ_SET_SLOT(cx, iterobj, JSSLOT_ITER_INDEX, INT_TO_JSVAL(i)); } } return JS_TRUE; } JS_PUBLIC_API(JSBool) JS_CheckAccess(JSContext *cx, JSObject *obj, jsid id, JSAccessMode mode, jsval *vp, uintN *attrsp) { CHECK_REQUEST(cx); return OBJ_CHECK_ACCESS(cx, obj, id, mode, vp, attrsp); } JS_PUBLIC_API(JSCheckAccessOp) JS_SetCheckObjectAccessCallback(JSRuntime *rt, JSCheckAccessOp acb) { JSCheckAccessOp oldacb; oldacb = rt->checkObjectAccess; rt->checkObjectAccess = acb; return oldacb; } static JSBool ReservedSlotIndexOK(JSContext *cx, JSObject *obj, JSClass *clasp, uint32 index, uint32 limit) { /* Check the computed, possibly per-instance, upper bound. */ if (clasp->reserveSlots) JS_LOCK_OBJ_VOID(cx, obj, limit += clasp->reserveSlots(cx, obj)); if (index >= limit) { JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_RESERVED_SLOT_RANGE); return JS_FALSE; } return JS_TRUE; } JS_PUBLIC_API(JSBool) JS_GetReservedSlot(JSContext *cx, JSObject *obj, uint32 index, jsval *vp) { JSClass *clasp; uint32 limit, slot; CHECK_REQUEST(cx); clasp = OBJ_GET_CLASS(cx, obj); limit = JSCLASS_RESERVED_SLOTS(clasp); if (index >= limit && !ReservedSlotIndexOK(cx, obj, clasp, index, limit)) return JS_FALSE; slot = JSSLOT_START(clasp) + index; *vp = OBJ_GET_REQUIRED_SLOT(cx, obj, slot); return JS_TRUE; } JS_PUBLIC_API(JSBool) JS_SetReservedSlot(JSContext *cx, JSObject *obj, uint32 index, jsval v) { JSClass *clasp; uint32 limit, slot; CHECK_REQUEST(cx); clasp = OBJ_GET_CLASS(cx, obj); limit = JSCLASS_RESERVED_SLOTS(clasp); if (index >= limit && !ReservedSlotIndexOK(cx, obj, clasp, index, limit)) return JS_FALSE; slot = JSSLOT_START(clasp) + index; return OBJ_SET_REQUIRED_SLOT(cx, obj, slot, v); } #ifdef JS_THREADSAFE JS_PUBLIC_API(jsrefcount) JS_HoldPrincipals(JSContext *cx, JSPrincipals *principals) { return JS_ATOMIC_INCREMENT(&principals->refcount); } JS_PUBLIC_API(jsrefcount) JS_DropPrincipals(JSContext *cx, JSPrincipals *principals) { jsrefcount rc = JS_ATOMIC_DECREMENT(&principals->refcount); if (rc == 0) principals->destroy(cx, principals); return rc; } #endif JS_PUBLIC_API(JSPrincipalsTranscoder) JS_SetPrincipalsTranscoder(JSRuntime *rt, JSPrincipalsTranscoder px) { JSPrincipalsTranscoder oldpx; oldpx = rt->principalsTranscoder; rt->principalsTranscoder = px; return oldpx; } JS_PUBLIC_API(JSObjectPrincipalsFinder) JS_SetObjectPrincipalsFinder(JSRuntime *rt, JSObjectPrincipalsFinder fop) { JSObjectPrincipalsFinder oldfop; oldfop = rt->findObjectPrincipals; rt->findObjectPrincipals = fop; return oldfop; } JS_PUBLIC_API(JSFunction *) JS_NewFunction(JSContext *cx, JSNative native, uintN nargs, uintN flags, JSObject *parent, const char *name) { JSAtom *atom; CHECK_REQUEST(cx); if (!name) { atom = NULL; } else { atom = js_Atomize(cx, name, strlen(name), 0); if (!atom) return NULL; } return js_NewFunction(cx, NULL, native, nargs, flags, parent, atom); } JS_PUBLIC_API(JSObject *) JS_CloneFunctionObject(JSContext *cx, JSObject *funobj, JSObject *parent) { CHECK_REQUEST(cx); if (OBJ_GET_CLASS(cx, funobj) != &js_FunctionClass) { /* Indicate we cannot clone this object. */ return funobj; } return js_CloneFunctionObject(cx, funobj, parent); } JS_PUBLIC_API(JSObject *) JS_GetFunctionObject(JSFunction *fun) { return fun->object; } JS_PUBLIC_API(const char *) JS_GetFunctionName(JSFunction *fun) { return fun->atom ? JS_GetStringBytes(ATOM_TO_STRING(fun->atom)) : js_anonymous_str; } JS_PUBLIC_API(JSString *) JS_GetFunctionId(JSFunction *fun) { return fun->atom ? ATOM_TO_STRING(fun->atom) : NULL; } JS_PUBLIC_API(uintN) JS_GetFunctionFlags(JSFunction *fun) { #ifdef MOZILLA_1_8_BRANCH uintN flags = fun->flags; return JSFUN_DISJOINT_FLAGS(flags) | (JSFUN_GETTER_TEST(flags) ? JSFUN_GETTER : 0) | (JSFUN_SETTER_TEST(flags) ? JSFUN_SETTER : 0) | (JSFUN_BOUND_METHOD_TEST(flags) ? JSFUN_BOUND_METHOD : 0) | (JSFUN_HEAVYWEIGHT_TEST(flags) ? JSFUN_HEAVYWEIGHT : 0); #else return fun->flags; #endif } JS_PUBLIC_API(uint16) JS_GetFunctionArity(JSFunction *fun) { return fun->nargs; } JS_PUBLIC_API(JSBool) JS_ObjectIsFunction(JSContext *cx, JSObject *obj) { return OBJ_GET_CLASS(cx, obj) == &js_FunctionClass; } JS_STATIC_DLL_CALLBACK(JSBool) js_generic_native_method_dispatcher(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) { jsval fsv; JSFunctionSpec *fs; JSObject *tmp; if (!JS_GetReservedSlot(cx, JSVAL_TO_OBJECT(argv[-2]), 0, &fsv)) return JS_FALSE; fs = (JSFunctionSpec *) JSVAL_TO_PRIVATE(fsv); /* * We know that argv[0] is valid because JS_DefineFunctions, which is our * only (indirect) referrer, defined us as requiring at least one argument * (notice how it passes fs->nargs + 1 as the next-to-last argument to * JS_DefineFunction). */ if (JSVAL_IS_PRIMITIVE(argv[0])) { /* * Make sure that this is an object or null, as required by the generic * functions. */ if (!js_ValueToObject(cx, argv[0], &tmp)) return JS_FALSE; argv[0] = OBJECT_TO_JSVAL(tmp); } /* * Copy all actual (argc) and required but missing (fs->nargs + 1 - argc) * args down over our |this| parameter, argv[-1], which is almost always * the class constructor object, e.g. Array. Then call the corresponding * prototype native method with our first argument passed as |this|. */ memmove(argv - 1, argv, JS_MAX(fs->nargs + 1U, argc) * sizeof(jsval)); /* * Follow Function.prototype.apply and .call by using the global object as * the 'this' param if no args. */ JS_ASSERT(cx->fp->argv == argv); tmp = js_ComputeThis(cx, JSVAL_TO_OBJECT(argv[-1]), argv); if (!tmp) return JS_FALSE; cx->fp->thisp = tmp; /* * Protect against argc - 1 underflowing below. By calling js_ComputeThis, * we made it as if the static was called with one parameter. */ if (argc == 0) argc = 1; return fs->call(cx, JSVAL_TO_OBJECT(argv[-1]), argc - 1, argv, rval); } JS_PUBLIC_API(JSBool) JS_DefineFunctions(JSContext *cx, JSObject *obj, JSFunctionSpec *fs) { uintN flags; JSObject *ctor; JSFunction *fun; CHECK_REQUEST(cx); ctor = NULL; for (; fs->name; fs++) { /* High bits of fs->extra are reserved. */ JS_ASSERT((fs->extra & 0xFFFF0000) == 0); flags = fs->flags; /* * Define a generic arity N+1 static method for the arity N prototype * method if flags contains JSFUN_GENERIC_NATIVE. */ if (flags & JSFUN_GENERIC_NATIVE) { if (!ctor) { ctor = JS_GetConstructor(cx, obj); if (!ctor) return JS_FALSE; } flags &= ~JSFUN_GENERIC_NATIVE; fun = JS_DefineFunction(cx, ctor, fs->name, js_generic_native_method_dispatcher, fs->nargs + 1, flags); if (!fun) return JS_FALSE; fun->u.n.extra = (uint16)fs->extra; /* * As jsapi.h notes, fs must point to storage that lives as long * as fun->object lives. */ if (!JS_SetReservedSlot(cx, fun->object, 0, PRIVATE_TO_JSVAL(fs))) return JS_FALSE; } fun = JS_DefineFunction(cx, obj, fs->name, fs->call, fs->nargs, flags); if (!fun) return JS_FALSE; fun->u.n.extra = (uint16)fs->extra; } return JS_TRUE; } JS_PUBLIC_API(JSFunction *) JS_DefineFunction(JSContext *cx, JSObject *obj, const char *name, JSNative call, uintN nargs, uintN attrs) { JSAtom *atom; CHECK_REQUEST(cx); atom = js_Atomize(cx, name, strlen(name), 0); if (!atom) return NULL; return js_DefineFunction(cx, obj, atom, call, nargs, attrs); } JS_PUBLIC_API(JSFunction *) JS_DefineUCFunction(JSContext *cx, JSObject *obj, const jschar *name, size_t namelen, JSNative call, uintN nargs, uintN attrs) { JSAtom *atom; atom = js_AtomizeChars(cx, name, AUTO_NAMELEN(name, namelen), 0); if (!atom) return NULL; return js_DefineFunction(cx, obj, atom, call, nargs, attrs); } static JSScript * CompileTokenStream(JSContext *cx, JSObject *obj, JSTokenStream *ts, void *tempMark, JSBool *eofp) { JSBool eof; JSArenaPool codePool, notePool; JSCodeGenerator cg; JSScript *script; CHECK_REQUEST(cx); eof = JS_FALSE; JS_InitArenaPool(&codePool, "code", 1024, sizeof(jsbytecode)); JS_InitArenaPool(¬ePool, "note", 1024, sizeof(jssrcnote)); if (!js_InitCodeGenerator(cx, &cg, &codePool, ¬ePool, ts->filename, ts->lineno, ts->principals)) { script = NULL; } else if (!js_CompileTokenStream(cx, obj, ts, &cg)) { script = NULL; eof = (ts->flags & TSF_EOF) != 0; } else { script = js_NewScriptFromCG(cx, &cg, NULL); } if (eofp) *eofp = eof; if (!js_CloseTokenStream(cx, ts)) { if (script) js_DestroyScript(cx, script); script = NULL; } cg.tempMark = tempMark; js_FinishCodeGenerator(cx, &cg); JS_FinishArenaPool(&codePool); JS_FinishArenaPool(¬ePool); return script; } JS_PUBLIC_API(JSScript *) JS_CompileScript(JSContext *cx, JSObject *obj, const char *bytes, size_t length, const char *filename, uintN lineno) { jschar *chars; JSScript *script; CHECK_REQUEST(cx); chars = js_InflateString(cx, bytes, &length); if (!chars) return NULL; script = JS_CompileUCScript(cx, obj, chars, length, filename, lineno); JS_free(cx, chars); return script; } JS_PUBLIC_API(JSScript *) JS_CompileScriptForPrincipals(JSContext *cx, JSObject *obj, JSPrincipals *principals, const char *bytes, size_t length, const char *filename, uintN lineno) { jschar *chars; JSScript *script; CHECK_REQUEST(cx); chars = js_InflateString(cx, bytes, &length); if (!chars) return NULL; script = JS_CompileUCScriptForPrincipals(cx, obj, principals, chars, length, filename, lineno); JS_free(cx, chars); return script; } JS_PUBLIC_API(JSScript *) JS_CompileUCScript(JSContext *cx, JSObject *obj, const jschar *chars, size_t length, const char *filename, uintN lineno) { CHECK_REQUEST(cx); return JS_CompileUCScriptForPrincipals(cx, obj, NULL, chars, length, filename, lineno); } #define LAST_FRAME_EXCEPTION_CHECK(cx,result) \ JS_BEGIN_MACRO \ if (!(result) && !((cx)->options & JSOPTION_DONT_REPORT_UNCAUGHT)) \ js_ReportUncaughtException(cx); \ JS_END_MACRO #define LAST_FRAME_CHECKS(cx,result) \ JS_BEGIN_MACRO \ if (!(cx)->fp) { \ (cx)->weakRoots.lastInternalResult = JSVAL_NULL; \ LAST_FRAME_EXCEPTION_CHECK(cx, result); \ } \ JS_END_MACRO JS_PUBLIC_API(JSScript *) JS_CompileUCScriptForPrincipals(JSContext *cx, JSObject *obj, JSPrincipals *principals, const jschar *chars, size_t length, const char *filename, uintN lineno) { void *mark; JSTokenStream *ts; JSScript *script; CHECK_REQUEST(cx); mark = JS_ARENA_MARK(&cx->tempPool); ts = js_NewTokenStream(cx, chars, length, filename, lineno, principals); if (!ts) return NULL; script = CompileTokenStream(cx, obj, ts, mark, NULL); LAST_FRAME_CHECKS(cx, script); return script; } JS_PUBLIC_API(JSBool) JS_BufferIsCompilableUnit(JSContext *cx, JSObject *obj, const char *bytes, size_t length) { jschar *chars; JSBool result; JSExceptionState *exnState; void *tempMark; JSTokenStream *ts; JSErrorReporter older; CHECK_REQUEST(cx); chars = js_InflateString(cx, bytes, &length); if (!chars) return JS_TRUE; /* * Return true on any out-of-memory error, so our caller doesn't try to * collect more buffered source. */ result = JS_TRUE; exnState = JS_SaveExceptionState(cx); tempMark = JS_ARENA_MARK(&cx->tempPool); ts = js_NewTokenStream(cx, chars, length, NULL, 0, NULL); if (ts) { older = JS_SetErrorReporter(cx, NULL); if (!js_ParseTokenStream(cx, obj, ts) && (ts->flags & TSF_UNEXPECTED_EOF)) { /* * We ran into an error. If it was because we ran out of source, * we return false, so our caller will know to try to collect more * buffered source. */ result = JS_FALSE; } JS_SetErrorReporter(cx, older); js_CloseTokenStream(cx, ts); JS_ARENA_RELEASE(&cx->tempPool, tempMark); } JS_free(cx, chars); JS_RestoreExceptionState(cx, exnState); return result; } JS_PUBLIC_API(JSScript *) JS_CompileFile(JSContext *cx, JSObject *obj, const char *filename) { void *mark; JSTokenStream *ts; JSScript *script; CHECK_REQUEST(cx); mark = JS_ARENA_MARK(&cx->tempPool); ts = js_NewFileTokenStream(cx, filename, stdin); if (!ts) return NULL; script = CompileTokenStream(cx, obj, ts, mark, NULL); LAST_FRAME_CHECKS(cx, script); return script; } JS_PUBLIC_API(JSScript *) JS_CompileFileHandle(JSContext *cx, JSObject *obj, const char *filename, FILE *file) { return JS_CompileFileHandleForPrincipals(cx, obj, filename, file, NULL); } JS_PUBLIC_API(JSScript *) JS_CompileFileHandleForPrincipals(JSContext *cx, JSObject *obj, const char *filename, FILE *file, JSPrincipals *principals) { void *mark; JSTokenStream *ts; JSScript *script; CHECK_REQUEST(cx); mark = JS_ARENA_MARK(&cx->tempPool); ts = js_NewFileTokenStream(cx, NULL, file); if (!ts) return NULL; ts->filename = filename; /* XXXshaver js_NewFileTokenStream should do this, because it drops */ if (principals) { ts->principals = principals; JSPRINCIPALS_HOLD(cx, ts->principals); } script = CompileTokenStream(cx, obj, ts, mark, NULL); LAST_FRAME_CHECKS(cx, script); return script; } JS_PUBLIC_API(JSObject *) JS_NewScriptObject(JSContext *cx, JSScript *script) { JSObject *obj; obj = js_NewObject(cx, &js_ScriptClass, NULL, NULL); if (!obj) return NULL; if (script) { if (!JS_SetPrivate(cx, obj, script)) return NULL; script->object = obj; } return obj; } JS_PUBLIC_API(JSObject *) JS_GetScriptObject(JSScript *script) { return script->object; } JS_PUBLIC_API(void) JS_DestroyScript(JSContext *cx, JSScript *script) { CHECK_REQUEST(cx); js_DestroyScript(cx, script); } JS_PUBLIC_API(JSFunction *) JS_CompileFunction(JSContext *cx, JSObject *obj, const char *name, uintN nargs, const char **argnames, const char *bytes, size_t length, const char *filename, uintN lineno) { jschar *chars; JSFunction *fun; CHECK_REQUEST(cx); chars = js_InflateString(cx, bytes, &length); if (!chars) return NULL; fun = JS_CompileUCFunction(cx, obj, name, nargs, argnames, chars, length, filename, lineno); JS_free(cx, chars); return fun; } JS_PUBLIC_API(JSFunction *) JS_CompileFunctionForPrincipals(JSContext *cx, JSObject *obj, JSPrincipals *principals, const char *name, uintN nargs, const char **argnames, const char *bytes, size_t length, const char *filename, uintN lineno) { jschar *chars; JSFunction *fun; CHECK_REQUEST(cx); chars = js_InflateString(cx, bytes, &length); if (!chars) return NULL; fun = JS_CompileUCFunctionForPrincipals(cx, obj, principals, name, nargs, argnames, chars, length, filename, lineno); JS_free(cx, chars); return fun; } JS_PUBLIC_API(JSFunction *) JS_CompileUCFunction(JSContext *cx, JSObject *obj, const char *name, uintN nargs, const char **argnames, const jschar *chars, size_t length, const char *filename, uintN lineno) { CHECK_REQUEST(cx); return JS_CompileUCFunctionForPrincipals(cx, obj, NULL, name, nargs, argnames, chars, length, filename, lineno); } JS_PUBLIC_API(JSFunction *) JS_CompileUCFunctionForPrincipals(JSContext *cx, JSObject *obj, JSPrincipals *principals, const char *name, uintN nargs, const char **argnames, const jschar *chars, size_t length, const char *filename, uintN lineno) { void *mark; JSTokenStream *ts; JSFunction *fun; JSAtom *funAtom, *argAtom; uintN i; CHECK_REQUEST(cx); mark = JS_ARENA_MARK(&cx->tempPool); ts = js_NewTokenStream(cx, chars, length, filename, lineno, principals); if (!ts) { fun = NULL; goto out; } if (!name) { funAtom = NULL; } else { funAtom = js_Atomize(cx, name, strlen(name), 0); if (!funAtom) { fun = NULL; goto out; } } fun = js_NewFunction(cx, NULL, NULL, nargs, 0, obj, funAtom); if (!fun) goto out; if (nargs) { for (i = 0; i < nargs; i++) { argAtom = js_Atomize(cx, argnames[i], strlen(argnames[i]), 0); if (!argAtom) break; if (!js_AddHiddenProperty(cx, fun->object, ATOM_TO_JSID(argAtom), js_GetArgument, js_SetArgument, SPROP_INVALID_SLOT, JSPROP_PERMANENT | JSPROP_SHARED, SPROP_HAS_SHORTID, i)) { break; } } if (i < nargs) { fun = NULL; goto out; } } if (!js_CompileFunctionBody(cx, ts, fun)) { fun = NULL; goto out; } if (obj && funAtom) { if (!OBJ_DEFINE_PROPERTY(cx, obj, ATOM_TO_JSID(funAtom), OBJECT_TO_JSVAL(fun->object), NULL, NULL, JSPROP_ENUMERATE, NULL)) { return NULL; } } out: if (ts) js_CloseTokenStream(cx, ts); JS_ARENA_RELEASE(&cx->tempPool, mark); LAST_FRAME_CHECKS(cx, fun); return fun; } JS_PUBLIC_API(JSString *) JS_DecompileScript(JSContext *cx, JSScript *script, const char *name, uintN indent) { JSPrinter *jp; JSString *str; CHECK_REQUEST(cx); jp = js_NewPrinter(cx, name, indent & ~JS_DONT_PRETTY_PRINT, !(indent & JS_DONT_PRETTY_PRINT)); if (!jp) return NULL; if (js_DecompileScript(jp, script)) str = js_GetPrinterOutput(jp); else str = NULL; js_DestroyPrinter(jp); return str; } JS_PUBLIC_API(JSString *) JS_DecompileFunction(JSContext *cx, JSFunction *fun, uintN indent) { JSPrinter *jp; JSString *str; CHECK_REQUEST(cx); jp = js_NewPrinter(cx, JS_GetFunctionName(fun), indent & ~JS_DONT_PRETTY_PRINT, !(indent & JS_DONT_PRETTY_PRINT)); if (!jp) return NULL; if (js_DecompileFunction(jp, fun)) str = js_GetPrinterOutput(jp); else str = NULL; js_DestroyPrinter(jp); return str; } JS_PUBLIC_API(JSString *) JS_DecompileFunctionBody(JSContext *cx, JSFunction *fun, uintN indent) { JSPrinter *jp; JSString *str; CHECK_REQUEST(cx); jp = js_NewPrinter(cx, JS_GetFunctionName(fun), indent & ~JS_DONT_PRETTY_PRINT, !(indent & JS_DONT_PRETTY_PRINT)); if (!jp) return NULL; if (js_DecompileFunctionBody(jp, fun)) str = js_GetPrinterOutput(jp); else str = NULL; js_DestroyPrinter(jp); return str; } JS_PUBLIC_API(JSBool) JS_ExecuteScript(JSContext *cx, JSObject *obj, JSScript *script, jsval *rval) { JSBool ok; CHECK_REQUEST(cx); ok = js_Execute(cx, obj, script, NULL, 0, rval); LAST_FRAME_CHECKS(cx, ok); return ok; } JS_PUBLIC_API(JSBool) JS_ExecuteScriptPart(JSContext *cx, JSObject *obj, JSScript *script, JSExecPart part, jsval *rval) { JSScript tmp; JSRuntime *rt; JSBool ok; /* Make a temporary copy of the JSScript structure and farble it a bit. */ tmp = *script; if (part == JSEXEC_PROLOG) { tmp.length = PTRDIFF(tmp.main, tmp.code, jsbytecode); } else { tmp.length -= PTRDIFF(tmp.main, tmp.code, jsbytecode); tmp.code = tmp.main; } /* Tell the debugger about our temporary copy of the script structure. */ rt = cx->runtime; if (rt->newScriptHook) { rt->newScriptHook(cx, tmp.filename, tmp.lineno, &tmp, NULL, rt->newScriptHookData); } /* Execute the farbled struct and tell the debugger to forget about it. */ ok = JS_ExecuteScript(cx, obj, &tmp, rval); if (rt->destroyScriptHook) rt->destroyScriptHook(cx, &tmp, rt->destroyScriptHookData); return ok; } /* Ancient uintN nbytes is part of API/ABI, so use size_t length local. */ JS_PUBLIC_API(JSBool) JS_EvaluateScript(JSContext *cx, JSObject *obj, const char *bytes, uintN nbytes, const char *filename, uintN lineno, jsval *rval) { size_t length = nbytes; jschar *chars; JSBool ok; CHECK_REQUEST(cx); chars = js_InflateString(cx, bytes, &length); if (!chars) return JS_FALSE; ok = JS_EvaluateUCScript(cx, obj, chars, length, filename, lineno, rval); JS_free(cx, chars); return ok; } /* Ancient uintN nbytes is part of API/ABI, so use size_t length local. */ JS_PUBLIC_API(JSBool) JS_EvaluateScriptForPrincipals(JSContext *cx, JSObject *obj, JSPrincipals *principals, const char *bytes, uintN nbytes, const char *filename, uintN lineno, jsval *rval) { size_t length = nbytes; jschar *chars; JSBool ok; CHECK_REQUEST(cx); chars = js_InflateString(cx, bytes, &length); if (!chars) return JS_FALSE; ok = JS_EvaluateUCScriptForPrincipals(cx, obj, principals, chars, length, filename, lineno, rval); JS_free(cx, chars); return ok; } JS_PUBLIC_API(JSBool) JS_EvaluateUCScript(JSContext *cx, JSObject *obj, const jschar *chars, uintN length, const char *filename, uintN lineno, jsval *rval) { CHECK_REQUEST(cx); return JS_EvaluateUCScriptForPrincipals(cx, obj, NULL, chars, length, filename, lineno, rval); } JS_PUBLIC_API(JSBool) JS_EvaluateUCScriptForPrincipals(JSContext *cx, JSObject *obj, JSPrincipals *principals, const jschar *chars, uintN length, const char *filename, uintN lineno, jsval *rval) { uint32 options; JSScript *script; JSBool ok; CHECK_REQUEST(cx); options = cx->options; cx->options = options | JSOPTION_COMPILE_N_GO; script = JS_CompileUCScriptForPrincipals(cx, obj, principals, chars, length, filename, lineno); cx->options = options; if (!script) return JS_FALSE; ok = js_Execute(cx, obj, script, NULL, 0, rval); LAST_FRAME_CHECKS(cx, ok); JS_DestroyScript(cx, script); return ok; } JS_PUBLIC_API(JSBool) JS_CallFunction(JSContext *cx, JSObject *obj, JSFunction *fun, uintN argc, jsval *argv, jsval *rval) { JSBool ok; CHECK_REQUEST(cx); ok = js_InternalCall(cx, obj, OBJECT_TO_JSVAL(fun->object), argc, argv, rval); LAST_FRAME_CHECKS(cx, ok); return ok; } JS_PUBLIC_API(JSBool) JS_CallFunctionName(JSContext *cx, JSObject *obj, const char *name, uintN argc, jsval *argv, jsval *rval) { JSBool ok; jsval fval; CHECK_REQUEST(cx); #if JS_HAS_XML_SUPPORT if (OBJECT_IS_XML(cx, obj)) { JSXMLObjectOps *ops; JSAtom *atom; ops = (JSXMLObjectOps *) obj->map->ops; atom = js_Atomize(cx, name, strlen(name), 0); if (!atom) return JS_FALSE; obj = ops->getMethod(cx, obj, ATOM_TO_JSID(atom), &fval); if (!obj) return JS_FALSE; } else #endif if (!JS_GetProperty(cx, obj, name, &fval)) return JS_FALSE; ok = js_InternalCall(cx, obj, fval, argc, argv, rval); LAST_FRAME_CHECKS(cx, ok); return ok; } JS_PUBLIC_API(JSBool) JS_CallFunctionValue(JSContext *cx, JSObject *obj, jsval fval, uintN argc, jsval *argv, jsval *rval) { JSBool ok; CHECK_REQUEST(cx); ok = js_InternalCall(cx, obj, fval, argc, argv, rval); LAST_FRAME_CHECKS(cx, ok); return ok; } JS_PUBLIC_API(JSBranchCallback) JS_SetBranchCallback(JSContext *cx, JSBranchCallback cb) { JSBranchCallback oldcb; oldcb = cx->branchCallback; cx->branchCallback = cb; return oldcb; } JS_PUBLIC_API(JSBool) JS_IsRunning(JSContext *cx) { return cx->fp != NULL; } JS_PUBLIC_API(JSBool) JS_IsConstructing(JSContext *cx) { return cx->fp && (cx->fp->flags & JSFRAME_CONSTRUCTING); } JS_FRIEND_API(JSBool) JS_IsAssigning(JSContext *cx) { JSStackFrame *fp; jsbytecode *pc; for (fp = cx->fp; fp && !fp->script; fp = fp->down) continue; if (!fp || !(pc = fp->pc)) return JS_FALSE; return (js_CodeSpec[*pc].format & JOF_ASSIGNING) != 0; } JS_PUBLIC_API(void) JS_SetCallReturnValue2(JSContext *cx, jsval v) { #if JS_HAS_LVALUE_RETURN cx->rval2 = v; cx->rval2set = JS_TRUE; #endif } JS_PUBLIC_API(JSStackFrame *) JS_SaveFrameChain(JSContext *cx) { JSStackFrame *fp; fp = cx->fp; if (!fp) return fp; JS_ASSERT(!fp->dormantNext); fp->dormantNext = cx->dormantFrameChain; cx->dormantFrameChain = fp; cx->fp = NULL; return fp; } JS_PUBLIC_API(void) JS_RestoreFrameChain(JSContext *cx, JSStackFrame *fp) { JS_ASSERT(!cx->fp); if (!fp) return; JS_ASSERT(cx->dormantFrameChain == fp); cx->fp = fp; cx->dormantFrameChain = fp->dormantNext; fp->dormantNext = NULL; } /************************************************************************/ JS_PUBLIC_API(JSString *) JS_NewString(JSContext *cx, char *bytes, size_t nbytes) { size_t length = nbytes; jschar *chars; JSString *str; CHECK_REQUEST(cx); /* Make a UTF-16 vector from the 8-bit char codes in bytes. */ chars = js_InflateString(cx, bytes, &length); if (!chars) return NULL; /* Free chars (but not bytes, which caller frees on error) if we fail. */ str = js_NewString(cx, chars, length, 0); if (!str) { JS_free(cx, chars); return NULL; } /* Hand off bytes to the deflated string cache, if possible. */ if (!js_SetStringBytes(cx->runtime, str, bytes, nbytes)) JS_free(cx, bytes); return str; } JS_PUBLIC_API(JSString *) JS_NewStringCopyN(JSContext *cx, const char *s, size_t n) { jschar *js; JSString *str; CHECK_REQUEST(cx); js = js_InflateString(cx, s, &n); if (!js) return NULL; str = js_NewString(cx, js, n, 0); if (!str) JS_free(cx, js); return str; } JS_PUBLIC_API(JSString *) JS_NewStringCopyZ(JSContext *cx, const char *s) { size_t n; jschar *js; JSString *str; CHECK_REQUEST(cx); if (!s) return cx->runtime->emptyString; n = strlen(s); js = js_InflateString(cx, s, &n); if (!js) return NULL; str = js_NewString(cx, js, n, 0); if (!str) JS_free(cx, js); return str; } JS_PUBLIC_API(JSString *) JS_InternString(JSContext *cx, const char *s) { JSAtom *atom; CHECK_REQUEST(cx); atom = js_Atomize(cx, s, strlen(s), ATOM_INTERNED); if (!atom) return NULL; return ATOM_TO_STRING(atom); } JS_PUBLIC_API(JSString *) JS_NewUCString(JSContext *cx, jschar *chars, size_t length) { CHECK_REQUEST(cx); return js_NewString(cx, chars, length, 0); } JS_PUBLIC_API(JSString *) JS_NewUCStringCopyN(JSContext *cx, const jschar *s, size_t n) { CHECK_REQUEST(cx); return js_NewStringCopyN(cx, s, n, 0); } JS_PUBLIC_API(JSString *) JS_NewUCStringCopyZ(JSContext *cx, const jschar *s) { CHECK_REQUEST(cx); if (!s) return cx->runtime->emptyString; return js_NewStringCopyZ(cx, s, 0); } JS_PUBLIC_API(JSString *) JS_InternUCStringN(JSContext *cx, const jschar *s, size_t length) { JSAtom *atom; CHECK_REQUEST(cx); atom = js_AtomizeChars(cx, s, length, ATOM_INTERNED); if (!atom) return NULL; return ATOM_TO_STRING(atom); } JS_PUBLIC_API(JSString *) JS_InternUCString(JSContext *cx, const jschar *s) { return JS_InternUCStringN(cx, s, js_strlen(s)); } JS_PUBLIC_API(char *) JS_GetStringBytes(JSString *str) { JSRuntime *rt; char *bytes; rt = js_GetGCStringRuntime(str); bytes = js_GetStringBytes(rt, str); return bytes ? bytes : ""; } JS_PUBLIC_API(jschar *) JS_GetStringChars(JSString *str) { /* * API botch (again, shades of JS_GetStringBytes): we have no cx to pass * to js_UndependString (called by js_GetStringChars) for out-of-memory * error reports, so js_UndependString passes NULL and suppresses errors. * If it fails to convert a dependent string into an independent one, our * caller will not be guaranteed a \u0000 terminator as a backstop. This * may break some clients who already misbehave on embedded NULs. * * The gain of dependent strings, which cure quadratic and cubic growth * rate bugs in string concatenation, is worth this slight loss in API * compatibility. */ jschar *chars; chars = js_GetStringChars(str); return chars ? chars : JSSTRING_CHARS(str); } JS_PUBLIC_API(size_t) JS_GetStringLength(JSString *str) { return JSSTRING_LENGTH(str); } JS_PUBLIC_API(intN) JS_CompareStrings(JSString *str1, JSString *str2) { return js_CompareStrings(str1, str2); } JS_PUBLIC_API(JSString *) JS_NewGrowableString(JSContext *cx, jschar *chars, size_t length) { CHECK_REQUEST(cx); return js_NewString(cx, chars, length, GCF_MUTABLE); } JS_PUBLIC_API(JSString *) JS_NewDependentString(JSContext *cx, JSString *str, size_t start, size_t length) { CHECK_REQUEST(cx); return js_NewDependentString(cx, str, start, length, 0); } JS_PUBLIC_API(JSString *) JS_ConcatStrings(JSContext *cx, JSString *left, JSString *right) { CHECK_REQUEST(cx); return js_ConcatStrings(cx, left, right); } JS_PUBLIC_API(const jschar *) JS_UndependString(JSContext *cx, JSString *str) { CHECK_REQUEST(cx); return js_UndependString(cx, str); } JS_PUBLIC_API(JSBool) JS_MakeStringImmutable(JSContext *cx, JSString *str) { CHECK_REQUEST(cx); if (!js_UndependString(cx, str)) return JS_FALSE; *js_GetGCThingFlags(str) &= ~GCF_MUTABLE; return JS_TRUE; } JS_PUBLIC_API(JSBool) JS_EncodeCharacters(JSContext *cx, const jschar *src, size_t srclen, char *dst, size_t *dstlenp) { return js_DeflateStringToBuffer(cx, src, srclen, dst, dstlenp); } JS_PUBLIC_API(JSBool) JS_DecodeBytes(JSContext *cx, const char *src, size_t srclen, jschar *dst, size_t *dstlenp) { return js_InflateStringToBuffer(cx, src, srclen, dst, dstlenp); } JS_PUBLIC_API(JSBool) JS_CStringsAreUTF8() { #ifdef JS_C_STRINGS_ARE_UTF8 return JS_TRUE; #else return JS_FALSE; #endif } /************************************************************************/ JS_PUBLIC_API(void) JS_ReportError(JSContext *cx, const char *format, ...) { va_list ap; va_start(ap, format); js_ReportErrorVA(cx, JSREPORT_ERROR, format, ap); va_end(ap); } JS_PUBLIC_API(void) JS_ReportErrorNumber(JSContext *cx, JSErrorCallback errorCallback, void *userRef, const uintN errorNumber, ...) { va_list ap; va_start(ap, errorNumber); js_ReportErrorNumberVA(cx, JSREPORT_ERROR, errorCallback, userRef, errorNumber, JS_TRUE, ap); va_end(ap); } JS_PUBLIC_API(void) JS_ReportErrorNumberUC(JSContext *cx, JSErrorCallback errorCallback, void *userRef, const uintN errorNumber, ...) { va_list ap; va_start(ap, errorNumber); js_ReportErrorNumberVA(cx, JSREPORT_ERROR, errorCallback, userRef, errorNumber, JS_FALSE, ap); va_end(ap); } JS_PUBLIC_API(JSBool) JS_ReportWarning(JSContext *cx, const char *format, ...) { va_list ap; JSBool ok; va_start(ap, format); ok = js_ReportErrorVA(cx, JSREPORT_WARNING, format, ap); va_end(ap); return ok; } JS_PUBLIC_API(JSBool) JS_ReportErrorFlagsAndNumber(JSContext *cx, uintN flags, JSErrorCallback errorCallback, void *userRef, const uintN errorNumber, ...) { va_list ap; JSBool ok; va_start(ap, errorNumber); ok = js_ReportErrorNumberVA(cx, flags, errorCallback, userRef, errorNumber, JS_TRUE, ap); va_end(ap); return ok; } JS_PUBLIC_API(JSBool) JS_ReportErrorFlagsAndNumberUC(JSContext *cx, uintN flags, JSErrorCallback errorCallback, void *userRef, const uintN errorNumber, ...) { va_list ap; JSBool ok; va_start(ap, errorNumber); ok = js_ReportErrorNumberVA(cx, flags, errorCallback, userRef, errorNumber, JS_FALSE, ap); va_end(ap); return ok; } JS_PUBLIC_API(void) JS_ReportOutOfMemory(JSContext *cx) { js_ReportOutOfMemory(cx); } JS_PUBLIC_API(JSErrorReporter) JS_SetErrorReporter(JSContext *cx, JSErrorReporter er) { JSErrorReporter older; older = cx->errorReporter; cx->errorReporter = er; return older; } /************************************************************************/ /* * Regular Expressions. */ JS_PUBLIC_API(JSObject *) JS_NewRegExpObject(JSContext *cx, char *bytes, size_t length, uintN flags) { jschar *chars; JSObject *obj; CHECK_REQUEST(cx); chars = js_InflateString(cx, bytes, &length); if (!chars) return NULL; obj = js_NewRegExpObject(cx, NULL, chars, length, flags); JS_free(cx, chars); return obj; } JS_PUBLIC_API(JSObject *) JS_NewUCRegExpObject(JSContext *cx, jschar *chars, size_t length, uintN flags) { CHECK_REQUEST(cx); return js_NewRegExpObject(cx, NULL, chars, length, flags); } JS_PUBLIC_API(void) JS_SetRegExpInput(JSContext *cx, JSString *input, JSBool multiline) { JSRegExpStatics *res; CHECK_REQUEST(cx); /* No locking required, cx is thread-private and input must be live. */ res = &cx->regExpStatics; res->input = input; res->multiline = multiline; cx->runtime->gcPoke = JS_TRUE; } JS_PUBLIC_API(void) JS_ClearRegExpStatics(JSContext *cx) { JSRegExpStatics *res; /* No locking required, cx is thread-private and input must be live. */ res = &cx->regExpStatics; res->input = NULL; res->multiline = JS_FALSE; res->parenCount = 0; res->lastMatch = res->lastParen = js_EmptySubString; res->leftContext = res->rightContext = js_EmptySubString; cx->runtime->gcPoke = JS_TRUE; } JS_PUBLIC_API(void) JS_ClearRegExpRoots(JSContext *cx) { JSRegExpStatics *res; /* No locking required, cx is thread-private and input must be live. */ res = &cx->regExpStatics; res->input = NULL; cx->runtime->gcPoke = JS_TRUE; } /* TODO: compile, execute, get/set other statics... */ /************************************************************************/ JS_PUBLIC_API(void) JS_SetLocaleCallbacks(JSContext *cx, JSLocaleCallbacks *callbacks) { cx->localeCallbacks = callbacks; } JS_PUBLIC_API(JSLocaleCallbacks *) JS_GetLocaleCallbacks(JSContext *cx) { return cx->localeCallbacks; } /************************************************************************/ JS_PUBLIC_API(JSBool) JS_IsExceptionPending(JSContext *cx) { return (JSBool) cx->throwing; } JS_PUBLIC_API(JSBool) JS_GetPendingException(JSContext *cx, jsval *vp) { CHECK_REQUEST(cx); if (!cx->throwing) return JS_FALSE; *vp = cx->exception; return JS_TRUE; } JS_PUBLIC_API(void) JS_SetPendingException(JSContext *cx, jsval v) { CHECK_REQUEST(cx); cx->throwing = JS_TRUE; cx->exception = v; } JS_PUBLIC_API(void) JS_ClearPendingException(JSContext *cx) { cx->throwing = JS_FALSE; cx->exception = JSVAL_VOID; } JS_PUBLIC_API(JSBool) JS_ReportPendingException(JSContext *cx) { JSBool save, ok; CHECK_REQUEST(cx); /* * Set cx->creatingException to suppress the standard error-to-exception * conversion done by all {js,JS}_Report* functions except for OOM. The * cx->creatingException flag was added to suppress recursive divergence * under js_ErrorToException, but it serves for our purposes here too. */ save = cx->creatingException; cx->creatingException = JS_TRUE; ok = js_ReportUncaughtException(cx); cx->creatingException = save; return ok; } struct JSExceptionState { JSBool throwing; jsval exception; }; JS_PUBLIC_API(JSExceptionState *) JS_SaveExceptionState(JSContext *cx) { JSExceptionState *state; CHECK_REQUEST(cx); state = (JSExceptionState *) JS_malloc(cx, sizeof(JSExceptionState)); if (state) { state->throwing = JS_GetPendingException(cx, &state->exception); if (state->throwing && JSVAL_IS_GCTHING(state->exception)) js_AddRoot(cx, &state->exception, "JSExceptionState.exception"); } return state; } JS_PUBLIC_API(void) JS_RestoreExceptionState(JSContext *cx, JSExceptionState *state) { CHECK_REQUEST(cx); if (state) { if (state->throwing) JS_SetPendingException(cx, state->exception); else JS_ClearPendingException(cx); JS_DropExceptionState(cx, state); } } JS_PUBLIC_API(void) JS_DropExceptionState(JSContext *cx, JSExceptionState *state) { CHECK_REQUEST(cx); if (state) { if (state->throwing && JSVAL_IS_GCTHING(state->exception)) JS_RemoveRoot(cx, &state->exception); JS_free(cx, state); } } JS_PUBLIC_API(JSErrorReport *) JS_ErrorFromException(JSContext *cx, jsval v) { CHECK_REQUEST(cx); return js_ErrorFromException(cx, v); } JS_PUBLIC_API(JSBool) JS_ThrowReportedError(JSContext *cx, const char *message, JSErrorReport *reportp) { return js_ErrorToException(cx, message, reportp); } #ifdef JS_THREADSAFE /* * Get the owning thread id of a context. Returns 0 if the context is not * owned by any thread. */ JS_PUBLIC_API(jsword) JS_GetContextThread(JSContext *cx) { return JS_THREAD_ID(cx); } /* * Set the current thread as the owning thread of a context. Returns the * old owning thread id, or -1 if the operation failed. */ JS_PUBLIC_API(jsword) JS_SetContextThread(JSContext *cx) { jsword old = JS_THREAD_ID(cx); if (!js_SetContextThread(cx)) return -1; return old; } JS_PUBLIC_API(jsword) JS_ClearContextThread(JSContext *cx) { jsword old = JS_THREAD_ID(cx); js_ClearContextThread(cx); return old; } #endif /************************************************************************/ #if defined(XP_WIN) #include /* * Initialization routine for the JS DLL... */ /* * Global Instance handle... * In Win32 this is the module handle of the DLL. * * In Win16 this is the instance handle of the application * which loaded the DLL. */ #ifdef _WIN32 BOOL WINAPI DllMain (HINSTANCE hDLL, DWORD dwReason, LPVOID lpReserved) { return TRUE; } #else /* !_WIN32 */ int CALLBACK LibMain( HINSTANCE hInst, WORD wDataSeg, WORD cbHeapSize, LPSTR lpszCmdLine ) { return TRUE; } BOOL CALLBACK __loadds WEP(BOOL fSystemExit) { return TRUE; } #endif /* !_WIN32 */ #endif /* XP_WIN */ pacparser-1.4.5/src/spidermonkey/js/src/jsapi.h000066400000000000000000002467641464010763600215150ustar00rootroot00000000000000/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- * * ***** BEGIN LICENSE BLOCK ***** * Version: MPL 1.1/GPL 2.0/LGPL 2.1 * * The contents of this file are subject to the Mozilla Public License Version * 1.1 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * http://www.mozilla.org/MPL/ * * Software distributed under the License is distributed on an "AS IS" basis, * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License * for the specific language governing rights and limitations under the * License. * * The Original Code is Mozilla Communicator client code, released * March 31, 1998. * * The Initial Developer of the Original Code is * Netscape Communications Corporation. * Portions created by the Initial Developer are Copyright (C) 1998 * the Initial Developer. All Rights Reserved. * * Contributor(s): * * Alternatively, the contents of this file may be used under the terms of * either of the GNU General Public License Version 2 or later (the "GPL"), * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), * in which case the provisions of the GPL or the LGPL are applicable instead * of those above. If you wish to allow use of your version of this file only * under the terms of either the GPL or the LGPL, and not to allow others to * use your version of this file under the terms of the MPL, indicate your * decision by deleting the provisions above and replace them with the notice * and other provisions required by the GPL or the LGPL. If you do not delete * the provisions above, a recipient may use your version of this file under * the terms of any one of the MPL, the GPL or the LGPL. * * ***** END LICENSE BLOCK ***** */ #ifndef jsapi_h___ #define jsapi_h___ /* * JavaScript API. */ #include #include #include "jspubtd.h" JS_BEGIN_EXTERN_C /* * Type tags stored in the low bits of a jsval. */ #define JSVAL_OBJECT 0x0 /* untagged reference to object */ #define JSVAL_INT 0x1 /* tagged 31-bit integer value */ #define JSVAL_DOUBLE 0x2 /* tagged reference to double */ #define JSVAL_STRING 0x4 /* tagged reference to string */ #define JSVAL_BOOLEAN 0x6 /* tagged boolean value */ /* Type tag bitfield length and derived macros. */ #define JSVAL_TAGBITS 3 #define JSVAL_TAGMASK JS_BITMASK(JSVAL_TAGBITS) #define JSVAL_TAG(v) ((v) & JSVAL_TAGMASK) #define JSVAL_SETTAG(v,t) ((v) | (t)) #define JSVAL_CLRTAG(v) ((v) & ~(jsval)JSVAL_TAGMASK) #define JSVAL_ALIGN JS_BIT(JSVAL_TAGBITS) /* Predicates for type testing. */ #define JSVAL_IS_OBJECT(v) (JSVAL_TAG(v) == JSVAL_OBJECT) #define JSVAL_IS_NUMBER(v) (JSVAL_IS_INT(v) || JSVAL_IS_DOUBLE(v)) #define JSVAL_IS_INT(v) (((v) & JSVAL_INT) && (v) != JSVAL_VOID) #define JSVAL_IS_DOUBLE(v) (JSVAL_TAG(v) == JSVAL_DOUBLE) #define JSVAL_IS_STRING(v) (JSVAL_TAG(v) == JSVAL_STRING) #define JSVAL_IS_BOOLEAN(v) (JSVAL_TAG(v) == JSVAL_BOOLEAN) #define JSVAL_IS_NULL(v) ((v) == JSVAL_NULL) #define JSVAL_IS_VOID(v) ((v) == JSVAL_VOID) #define JSVAL_IS_PRIMITIVE(v) (!JSVAL_IS_OBJECT(v) || JSVAL_IS_NULL(v)) /* Objects, strings, and doubles are GC'ed. */ #define JSVAL_IS_GCTHING(v) (!((v) & JSVAL_INT) && !JSVAL_IS_BOOLEAN(v)) #define JSVAL_TO_GCTHING(v) ((void *)JSVAL_CLRTAG(v)) #define JSVAL_TO_OBJECT(v) ((JSObject *)JSVAL_TO_GCTHING(v)) #define JSVAL_TO_DOUBLE(v) ((jsdouble *)JSVAL_TO_GCTHING(v)) #define JSVAL_TO_STRING(v) ((JSString *)JSVAL_TO_GCTHING(v)) #define OBJECT_TO_JSVAL(obj) ((jsval)(obj)) #define DOUBLE_TO_JSVAL(dp) JSVAL_SETTAG((jsval)(dp), JSVAL_DOUBLE) #define STRING_TO_JSVAL(str) JSVAL_SETTAG((jsval)(str), JSVAL_STRING) /* Lock and unlock the GC thing held by a jsval. */ #define JSVAL_LOCK(cx,v) (JSVAL_IS_GCTHING(v) \ ? JS_LockGCThing(cx, JSVAL_TO_GCTHING(v)) \ : JS_TRUE) #define JSVAL_UNLOCK(cx,v) (JSVAL_IS_GCTHING(v) \ ? JS_UnlockGCThing(cx, JSVAL_TO_GCTHING(v)) \ : JS_TRUE) /* Domain limits for the jsval int type. */ #define JSVAL_INT_BITS 31 #define JSVAL_INT_POW2(n) ((jsval)1 << (n)) #define JSVAL_INT_MIN ((jsval)1 - JSVAL_INT_POW2(30)) #define JSVAL_INT_MAX (JSVAL_INT_POW2(30) - 1) #define INT_FITS_IN_JSVAL(i) ((jsuint)((i)+JSVAL_INT_MAX) <= 2*JSVAL_INT_MAX) #define JSVAL_TO_INT(v) ((jsint)(v) >> 1) #define INT_TO_JSVAL(i) (((jsval)(i) << 1) | JSVAL_INT) /* Convert between boolean and jsval. */ #define JSVAL_TO_BOOLEAN(v) ((JSBool)((v) >> JSVAL_TAGBITS)) #define BOOLEAN_TO_JSVAL(b) JSVAL_SETTAG((jsval)(b) << JSVAL_TAGBITS, \ JSVAL_BOOLEAN) /* A private data pointer (2-byte-aligned) can be stored as an int jsval. */ #define JSVAL_TO_PRIVATE(v) ((void *)((v) & ~JSVAL_INT)) #define PRIVATE_TO_JSVAL(p) ((jsval)(p) | JSVAL_INT) /* Property attributes, set in JSPropertySpec and passed to API functions. */ #define JSPROP_ENUMERATE 0x01 /* property is visible to for/in loop */ #define JSPROP_READONLY 0x02 /* not settable: assignment is no-op */ #define JSPROP_PERMANENT 0x04 /* property cannot be deleted */ #define JSPROP_EXPORTED 0x08 /* property is exported from object */ #define JSPROP_GETTER 0x10 /* property holds getter function */ #define JSPROP_SETTER 0x20 /* property holds setter function */ #define JSPROP_SHARED 0x40 /* don't allocate a value slot for this property; don't copy the property on set of the same-named property in an object that delegates to a prototype containing this property */ #define JSPROP_INDEX 0x80 /* name is actually (jsint) index */ /* Function flags, set in JSFunctionSpec and passed to JS_NewFunction etc. */ #define JSFUN_LAMBDA 0x08 /* expressed, not declared, function */ #define JSFUN_GETTER JSPROP_GETTER #define JSFUN_SETTER JSPROP_SETTER #define JSFUN_BOUND_METHOD 0x40 /* bind this to fun->object's parent */ #define JSFUN_HEAVYWEIGHT 0x80 /* activation requires a Call object */ #define JSFUN_DISJOINT_FLAGS(f) ((f) & 0x0f) #define JSFUN_GSFLAGS(f) ((f) & (JSFUN_GETTER | JSFUN_SETTER)) #ifdef MOZILLA_1_8_BRANCH /* * Squeeze three more bits into existing 8-bit flags by taking advantage of * the invalid combination (JSFUN_GETTER | JSFUN_SETTER). */ #define JSFUN_GETTER_TEST(f) (JSFUN_GSFLAGS(f) == JSFUN_GETTER) #define JSFUN_SETTER_TEST(f) (JSFUN_GSFLAGS(f) == JSFUN_SETTER) #define JSFUN_FLAGS_TEST(f,t) (JSFUN_GSFLAGS(~(f)) ? (f) & (t) : 0) #define JSFUN_BOUND_METHOD_TEST(f) JSFUN_FLAGS_TEST(f, JSFUN_BOUND_METHOD) #define JSFUN_HEAVYWEIGHT_TEST(f) JSFUN_FLAGS_TEST(f, JSFUN_HEAVYWEIGHT) #define JSFUN_GSFLAG2ATTR(f) (JSFUN_GETTER_TEST(f) ? JSPROP_GETTER : \ JSFUN_SETTER_TEST(f) ? JSPROP_SETTER : 0) #define JSFUN_THISP_FLAGS(f) (JSFUN_GSFLAGS(~(f)) ? 0 : \ (f) & JSFUN_THISP_PRIMITIVE) #define JSFUN_THISP_TEST(f,t) ((f) == (t) || (f) == JSFUN_THISP_PRIMITIVE) #define JSFUN_THISP_STRING 0x30 /* |this| may be a primitive string */ #define JSFUN_THISP_NUMBER 0x70 /* |this| may be a primitive number */ #define JSFUN_THISP_BOOLEAN 0xb0 /* |this| may be a primitive boolean */ #define JSFUN_THISP_PRIMITIVE 0xf0 /* |this| may be any primitive value */ #define JSFUN_FLAGS_MASK 0xf8 /* overlay JSFUN_* attributes */ #else #define JSFUN_GETTER_TEST(f) ((f) & JSFUN_GETTER) #define JSFUN_SETTER_TEST(f) ((f) & JSFUN_SETTER) #define JSFUN_BOUND_METHOD_TEST(f) ((f) & JSFUN_BOUND_METHOD) #define JSFUN_HEAVYWEIGHT_TEST(f) ((f) & JSFUN_HEAVYWEIGHT) #define JSFUN_GSFLAG2ATTR(f) JSFUN_GSFLAGS(f) #define JSFUN_THISP_FLAGS(f) (f) #define JSFUN_THISP_TEST(f,t) ((f) & t) #define JSFUN_THISP_STRING 0x0100 /* |this| may be a primitive string */ #define JSFUN_THISP_NUMBER 0x0200 /* |this| may be a primitive number */ #define JSFUN_THISP_BOOLEAN 0x0400 /* |this| may be a primitive boolean */ #define JSFUN_THISP_PRIMITIVE 0x0700 /* |this| may be any primitive value */ #define JSFUN_FLAGS_MASK 0x07f8 /* overlay JSFUN_* attributes -- note that bit #15 is used internally to flag interpreted functions */ #endif /* * Re-use JSFUN_LAMBDA, which applies only to scripted functions, for use in * JSFunctionSpec arrays that specify generic native prototype methods, i.e., * methods of a class prototype that are exposed as static methods taking an * extra leading argument: the generic |this| parameter. * * If you set this flag in a JSFunctionSpec struct's flags initializer, then * that struct must live at least as long as the native static method object * created due to this flag by JS_DefineFunctions or JS_InitClass. Typically * JSFunctionSpec structs are allocated in static arrays. */ #define JSFUN_GENERIC_NATIVE JSFUN_LAMBDA /* * Well-known JS values. The extern'd variables are initialized when the * first JSContext is created by JS_NewContext (see below). */ #define JSVAL_VOID INT_TO_JSVAL(0 - JSVAL_INT_POW2(30)) #define JSVAL_NULL OBJECT_TO_JSVAL(0) #define JSVAL_ZERO INT_TO_JSVAL(0) #define JSVAL_ONE INT_TO_JSVAL(1) #define JSVAL_FALSE BOOLEAN_TO_JSVAL(JS_FALSE) #define JSVAL_TRUE BOOLEAN_TO_JSVAL(JS_TRUE) /* * Microseconds since the epoch, midnight, January 1, 1970 UTC. See the * comment in jstypes.h regarding safe int64 usage. */ extern JS_PUBLIC_API(int64) JS_Now(); /* Don't want to export data, so provide accessors for non-inline jsvals. */ extern JS_PUBLIC_API(jsval) JS_GetNaNValue(JSContext *cx); extern JS_PUBLIC_API(jsval) JS_GetNegativeInfinityValue(JSContext *cx); extern JS_PUBLIC_API(jsval) JS_GetPositiveInfinityValue(JSContext *cx); extern JS_PUBLIC_API(jsval) JS_GetEmptyStringValue(JSContext *cx); /* * Format is a string of the following characters (spaces are insignificant), * specifying the tabulated type conversions: * * b JSBool Boolean * c uint16/jschar ECMA uint16, Unicode char * i int32 ECMA int32 * u uint32 ECMA uint32 * j int32 Rounded int32 (coordinate) * d jsdouble IEEE double * I jsdouble Integral IEEE double * s char * C string * S JSString * Unicode string, accessed by a JSString pointer * W jschar * Unicode character vector, 0-terminated (W for wide) * o JSObject * Object reference * f JSFunction * Function private * v jsval Argument value (no conversion) * * N/A Skip this argument (no vararg) * / N/A End of required arguments * * The variable argument list after format must consist of &b, &c, &s, e.g., * where those variables have the types given above. For the pointer types * char *, JSString *, and JSObject *, the pointed-at memory returned belongs * to the JS runtime, not to the calling native code. The runtime promises * to keep this memory valid so long as argv refers to allocated stack space * (so long as the native function is active). * * Fewer arguments than format specifies may be passed only if there is a / * in format after the last required argument specifier and argc is at least * the number of required arguments. More arguments than format specifies * may be passed without error; it is up to the caller to deal with trailing * unconverted arguments. */ extern JS_PUBLIC_API(JSBool) JS_ConvertArguments(JSContext *cx, uintN argc, jsval *argv, const char *format, ...); #ifdef va_start extern JS_PUBLIC_API(JSBool) JS_ConvertArgumentsVA(JSContext *cx, uintN argc, jsval *argv, const char *format, va_list ap); #endif /* * Inverse of JS_ConvertArguments: scan format and convert trailing arguments * into jsvals, GC-rooted if necessary by the JS stack. Return null on error, * and a pointer to the new argument vector on success. Also return a stack * mark on success via *markp, in which case the caller must eventually clean * up by calling JS_PopArguments. * * Note that the number of actual arguments supplied is specified exclusively * by format, so there is no argc parameter. */ extern JS_PUBLIC_API(jsval *) JS_PushArguments(JSContext *cx, void **markp, const char *format, ...); #ifdef va_start extern JS_PUBLIC_API(jsval *) JS_PushArgumentsVA(JSContext *cx, void **markp, const char *format, va_list ap); #endif extern JS_PUBLIC_API(void) JS_PopArguments(JSContext *cx, void *mark); #ifdef JS_ARGUMENT_FORMATTER_DEFINED /* * Add and remove a format string handler for JS_{Convert,Push}Arguments{,VA}. * The handler function has this signature (see jspubtd.h): * * JSBool MyArgumentFormatter(JSContext *cx, const char *format, * JSBool fromJS, jsval **vpp, va_list *app); * * It should return true on success, and return false after reporting an error * or detecting an already-reported error. * * For a given format string, for example "AA", the formatter is called from * JS_ConvertArgumentsVA like so: * * formatter(cx, "AA...", JS_TRUE, &sp, &ap); * * sp points into the arguments array on the JS stack, while ap points into * the stdarg.h va_list on the C stack. The JS_TRUE passed for fromJS tells * the formatter to convert zero or more jsvals at sp to zero or more C values * accessed via pointers-to-values at ap, updating both sp (via *vpp) and ap * (via *app) to point past the converted arguments and their result pointers * on the C stack. * * When called from JS_PushArgumentsVA, the formatter is invoked thus: * * formatter(cx, "AA...", JS_FALSE, &sp, &ap); * * where JS_FALSE for fromJS means to wrap the C values at ap according to the * format specifier and store them at sp, updating ap and sp appropriately. * * The "..." after "AA" is the rest of the format string that was passed into * JS_{Convert,Push}Arguments{,VA}. The actual format trailing substring used * in each Convert or PushArguments call is passed to the formatter, so that * one such function may implement several formats, in order to share code. * * Remove just forgets about any handler associated with format. Add does not * copy format, it points at the string storage allocated by the caller, which * is typically a string constant. If format is in dynamic storage, it is up * to the caller to keep the string alive until Remove is called. */ extern JS_PUBLIC_API(JSBool) JS_AddArgumentFormatter(JSContext *cx, const char *format, JSArgumentFormatter formatter); extern JS_PUBLIC_API(void) JS_RemoveArgumentFormatter(JSContext *cx, const char *format); #endif /* JS_ARGUMENT_FORMATTER_DEFINED */ extern JS_PUBLIC_API(JSBool) JS_ConvertValue(JSContext *cx, jsval v, JSType type, jsval *vp); extern JS_PUBLIC_API(JSBool) JS_ValueToObject(JSContext *cx, jsval v, JSObject **objp); extern JS_PUBLIC_API(JSFunction *) JS_ValueToFunction(JSContext *cx, jsval v); extern JS_PUBLIC_API(JSFunction *) JS_ValueToConstructor(JSContext *cx, jsval v); extern JS_PUBLIC_API(JSString *) JS_ValueToString(JSContext *cx, jsval v); extern JS_PUBLIC_API(JSBool) JS_ValueToNumber(JSContext *cx, jsval v, jsdouble *dp); /* * Convert a value to a number, then to an int32, according to the ECMA rules * for ToInt32. */ extern JS_PUBLIC_API(JSBool) JS_ValueToECMAInt32(JSContext *cx, jsval v, int32 *ip); /* * Convert a value to a number, then to a uint32, according to the ECMA rules * for ToUint32. */ extern JS_PUBLIC_API(JSBool) JS_ValueToECMAUint32(JSContext *cx, jsval v, uint32 *ip); /* * Convert a value to a number, then to an int32 if it fits by rounding to * nearest; but failing with an error report if the double is out of range * or unordered. */ extern JS_PUBLIC_API(JSBool) JS_ValueToInt32(JSContext *cx, jsval v, int32 *ip); /* * ECMA ToUint16, for mapping a jsval to a Unicode point. */ extern JS_PUBLIC_API(JSBool) JS_ValueToUint16(JSContext *cx, jsval v, uint16 *ip); extern JS_PUBLIC_API(JSBool) JS_ValueToBoolean(JSContext *cx, jsval v, JSBool *bp); extern JS_PUBLIC_API(JSType) JS_TypeOfValue(JSContext *cx, jsval v); extern JS_PUBLIC_API(const char *) JS_GetTypeName(JSContext *cx, JSType type); /************************************************************************/ /* * Initialization, locking, contexts, and memory allocation. */ #define JS_NewRuntime JS_Init #define JS_DestroyRuntime JS_Finish #define JS_LockRuntime JS_Lock #define JS_UnlockRuntime JS_Unlock extern JS_PUBLIC_API(JSRuntime *) JS_NewRuntime(uint32 maxbytes); extern JS_PUBLIC_API(void) JS_DestroyRuntime(JSRuntime *rt); extern JS_PUBLIC_API(void) JS_ShutDown(void); JS_PUBLIC_API(void *) JS_GetRuntimePrivate(JSRuntime *rt); JS_PUBLIC_API(void) JS_SetRuntimePrivate(JSRuntime *rt, void *data); #ifdef JS_THREADSAFE extern JS_PUBLIC_API(void) JS_BeginRequest(JSContext *cx); extern JS_PUBLIC_API(void) JS_EndRequest(JSContext *cx); /* Yield to pending GC operations, regardless of request depth */ extern JS_PUBLIC_API(void) JS_YieldRequest(JSContext *cx); extern JS_PUBLIC_API(jsrefcount) JS_SuspendRequest(JSContext *cx); extern JS_PUBLIC_API(void) JS_ResumeRequest(JSContext *cx, jsrefcount saveDepth); #ifdef __cplusplus JS_END_EXTERN_C class JSAutoRequest { public: JSAutoRequest(JSContext *cx) : mContext(cx), mSaveDepth(0) { JS_BeginRequest(mContext); } ~JSAutoRequest() { JS_EndRequest(mContext); } void suspend() { mSaveDepth = JS_SuspendRequest(mContext); } void resume() { JS_ResumeRequest(mContext, mSaveDepth); } protected: JSContext *mContext; jsrefcount mSaveDepth; #if 0 private: static void *operator new(size_t) CPP_THROW_NEW { return 0; }; static void operator delete(void *, size_t) { }; #endif }; JS_BEGIN_EXTERN_C #endif #endif /* JS_THREADSAFE */ extern JS_PUBLIC_API(void) JS_Lock(JSRuntime *rt); extern JS_PUBLIC_API(void) JS_Unlock(JSRuntime *rt); extern JS_PUBLIC_API(JSContextCallback) JS_SetContextCallback(JSRuntime *rt, JSContextCallback cxCallback); extern JS_PUBLIC_API(JSContext *) JS_NewContext(JSRuntime *rt, size_t stackChunkSize); extern JS_PUBLIC_API(void) JS_DestroyContext(JSContext *cx); extern JS_PUBLIC_API(void) JS_DestroyContextNoGC(JSContext *cx); extern JS_PUBLIC_API(void) JS_DestroyContextMaybeGC(JSContext *cx); extern JS_PUBLIC_API(void *) JS_GetContextPrivate(JSContext *cx); extern JS_PUBLIC_API(void) JS_SetContextPrivate(JSContext *cx, void *data); extern JS_PUBLIC_API(JSRuntime *) JS_GetRuntime(JSContext *cx); extern JS_PUBLIC_API(JSContext *) JS_ContextIterator(JSRuntime *rt, JSContext **iterp); extern JS_PUBLIC_API(JSVersion) JS_GetVersion(JSContext *cx); extern JS_PUBLIC_API(JSVersion) JS_SetVersion(JSContext *cx, JSVersion version); extern JS_PUBLIC_API(const char *) JS_VersionToString(JSVersion version); extern JS_PUBLIC_API(JSVersion) JS_StringToVersion(const char *string); /* * JS options are orthogonal to version, and may be freely composed with one * another as well as with version. * * JSOPTION_VAROBJFIX is recommended -- see the comments associated with the * prototypes for JS_ExecuteScript, JS_EvaluateScript, etc. */ #define JSOPTION_STRICT JS_BIT(0) /* warn on dubious practice */ #define JSOPTION_WERROR JS_BIT(1) /* convert warning to error */ #define JSOPTION_VAROBJFIX JS_BIT(2) /* make JS_EvaluateScript use the last object on its 'obj' param's scope chain as the ECMA 'variables object' */ #define JSOPTION_PRIVATE_IS_NSISUPPORTS \ JS_BIT(3) /* context private data points to an nsISupports subclass */ #define JSOPTION_COMPILE_N_GO JS_BIT(4) /* caller of JS_Compile*Script promises to execute compiled script once only; enables compile-time scope chain resolution of consts. */ #define JSOPTION_ATLINE JS_BIT(5) /* //@line number ["filename"] option supported for the XUL preprocessor and kindred beasts. */ #define JSOPTION_XML JS_BIT(6) /* EMCAScript for XML support: parse as a token, not backward compatible with the comment-hiding hack used in HTML script tags. */ #define JSOPTION_NATIVE_BRANCH_CALLBACK \ JS_BIT(7) /* the branch callback set by JS_SetBranchCallback may be called with a null script parameter, by native code that loops intensively */ #define JSOPTION_DONT_REPORT_UNCAUGHT \ JS_BIT(8) /* When returning from the outermost API call, prevent uncaught exceptions from being converted to error reports */ extern JS_PUBLIC_API(uint32) JS_GetOptions(JSContext *cx); extern JS_PUBLIC_API(uint32) JS_SetOptions(JSContext *cx, uint32 options); extern JS_PUBLIC_API(uint32) JS_ToggleOptions(JSContext *cx, uint32 options); extern JS_PUBLIC_API(const char *) JS_GetImplementationVersion(void); extern JS_PUBLIC_API(JSObject *) JS_GetGlobalObject(JSContext *cx); extern JS_PUBLIC_API(void) JS_SetGlobalObject(JSContext *cx, JSObject *obj); /* * Initialize standard JS class constructors, prototypes, and any top-level * functions and constants associated with the standard classes (e.g. isNaN * for Number). * * NB: This sets cx's global object to obj if it was null. */ extern JS_PUBLIC_API(JSBool) JS_InitStandardClasses(JSContext *cx, JSObject *obj); /* * Resolve id, which must contain either a string or an int, to a standard * class name in obj if possible, defining the class's constructor and/or * prototype and storing true in *resolved. If id does not name a standard * class or a top-level property induced by initializing a standard class, * store false in *resolved and just return true. Return false on error, * as usual for JSBool result-typed API entry points. * * This API can be called directly from a global object class's resolve op, * to define standard classes lazily. The class's enumerate op should call * JS_EnumerateStandardClasses(cx, obj), to define eagerly during for..in * loops any classes not yet resolved lazily. */ extern JS_PUBLIC_API(JSBool) JS_ResolveStandardClass(JSContext *cx, JSObject *obj, jsval id, JSBool *resolved); extern JS_PUBLIC_API(JSBool) JS_EnumerateStandardClasses(JSContext *cx, JSObject *obj); /* * Enumerate any already-resolved standard class ids into ida, or into a new * JSIdArray if ida is null. Return the augmented array on success, null on * failure with ida (if it was non-null on entry) destroyed. */ extern JS_PUBLIC_API(JSIdArray *) JS_EnumerateResolvedStandardClasses(JSContext *cx, JSObject *obj, JSIdArray *ida); extern JS_PUBLIC_API(JSBool) JS_GetClassObject(JSContext *cx, JSObject *obj, JSProtoKey key, JSObject **objp); extern JS_PUBLIC_API(JSObject *) JS_GetScopeChain(JSContext *cx); extern JS_PUBLIC_API(void *) JS_malloc(JSContext *cx, size_t nbytes); extern JS_PUBLIC_API(void *) JS_realloc(JSContext *cx, void *p, size_t nbytes); extern JS_PUBLIC_API(void) JS_free(JSContext *cx, void *p); extern JS_PUBLIC_API(char *) JS_strdup(JSContext *cx, const char *s); extern JS_PUBLIC_API(jsdouble *) JS_NewDouble(JSContext *cx, jsdouble d); extern JS_PUBLIC_API(JSBool) JS_NewDoubleValue(JSContext *cx, jsdouble d, jsval *rval); extern JS_PUBLIC_API(JSBool) JS_NewNumberValue(JSContext *cx, jsdouble d, jsval *rval); /* * A JS GC root is a pointer to a JSObject *, JSString *, or jsdouble * that * itself points into the GC heap (more recently, we support this extension: * a root may be a pointer to a jsval v for which JSVAL_IS_GCTHING(v) is true). * * Therefore, you never pass JSObject *obj to JS_AddRoot(cx, obj). You always * call JS_AddRoot(cx, &obj), passing obj by reference. And later, before obj * or the structure it is embedded within goes out of scope or is freed, you * must call JS_RemoveRoot(cx, &obj). * * Also, use JS_AddNamedRoot(cx, &structPtr->memberObj, "structPtr->memberObj") * in preference to JS_AddRoot(cx, &structPtr->memberObj), in order to identify * roots by their source callsites. This way, you can find the callsite while * debugging if you should fail to do JS_RemoveRoot(cx, &structPtr->memberObj) * before freeing structPtr's memory. */ extern JS_PUBLIC_API(JSBool) JS_AddRoot(JSContext *cx, void *rp); #ifdef NAME_ALL_GC_ROOTS #define JS_DEFINE_TO_TOKEN(def) #def #define JS_DEFINE_TO_STRING(def) JS_DEFINE_TO_TOKEN(def) #define JS_AddRoot(cx,rp) JS_AddNamedRoot((cx), (rp), (__FILE__ ":" JS_TOKEN_TO_STRING(__LINE__)) #endif extern JS_PUBLIC_API(JSBool) JS_AddNamedRoot(JSContext *cx, void *rp, const char *name); extern JS_PUBLIC_API(JSBool) JS_AddNamedRootRT(JSRuntime *rt, void *rp, const char *name); extern JS_PUBLIC_API(JSBool) JS_RemoveRoot(JSContext *cx, void *rp); extern JS_PUBLIC_API(JSBool) JS_RemoveRootRT(JSRuntime *rt, void *rp); /* * The last GC thing of each type (object, string, double, external string * types) created on a given context is kept alive until another thing of the * same type is created, using a newborn root in the context. These newborn * roots help native code protect newly-created GC-things from GC invocations * activated before those things can be rooted using local or global roots. * * However, the newborn roots can also entrain great gobs of garbage, so the * JS_GC entry point clears them for the context on which GC is being forced. * Embeddings may need to do likewise for all contexts. * * See the scoped local root API immediately below for a better way to manage * newborns in cases where native hooks (functions, getters, setters, etc.) * create many GC-things, potentially without connecting them to predefined * local roots such as *rval or argv[i] in an active native function. Using * JS_EnterLocalRootScope disables updating of the context's per-gc-thing-type * newborn roots, until control flow unwinds and leaves the outermost nesting * local root scope. */ extern JS_PUBLIC_API(void) JS_ClearNewbornRoots(JSContext *cx); /* * Scoped local root management allows native functions, getter/setters, etc. * to avoid worrying about the newborn root pigeon-holes, overloading local * roots allocated in argv and *rval, or ending up having to call JS_Add*Root * and JS_RemoveRoot to manage global roots temporarily. * * Instead, calling JS_EnterLocalRootScope and JS_LeaveLocalRootScope around * the body of the native hook causes the engine to allocate a local root for * each newborn created in between the two API calls, using a local root stack * associated with cx. For example: * * JSBool * my_GetProperty(JSContext *cx, JSObject *obj, jsval id, jsval *vp) * { * JSBool ok; * * if (!JS_EnterLocalRootScope(cx)) * return JS_FALSE; * ok = my_GetPropertyBody(cx, obj, id, vp); * JS_LeaveLocalRootScope(cx); * return ok; * } * * NB: JS_LeaveLocalRootScope must be called once for every prior successful * call to JS_EnterLocalRootScope. If JS_EnterLocalRootScope fails, you must * not make the matching JS_LeaveLocalRootScope call. * * JS_LeaveLocalRootScopeWithResult(cx, rval) is an alternative way to leave * a local root scope that protects a result or return value, by effectively * pushing it in the caller's local root scope. * * In case a native hook allocates many objects or other GC-things, but the * native protects some of those GC-things by storing them as property values * in an object that is itself protected, the hook can call JS_ForgetLocalRoot * to free the local root automatically pushed for the now-protected GC-thing. * * JS_ForgetLocalRoot works on any GC-thing allocated in the current local * root scope, but it's more time-efficient when called on references to more * recently created GC-things. Calling it successively on other than the most * recently allocated GC-thing will tend to average the time inefficiency, and * may risk O(n^2) growth rate, but in any event, you shouldn't allocate too * many local roots if you can root as you go (build a tree of objects from * the top down, forgetting each latest-allocated GC-thing immediately upon * linking it to its parent). */ extern JS_PUBLIC_API(JSBool) JS_EnterLocalRootScope(JSContext *cx); extern JS_PUBLIC_API(void) JS_LeaveLocalRootScope(JSContext *cx); extern JS_PUBLIC_API(void) JS_LeaveLocalRootScopeWithResult(JSContext *cx, jsval rval); extern JS_PUBLIC_API(void) JS_ForgetLocalRoot(JSContext *cx, void *thing); #ifdef __cplusplus JS_END_EXTERN_C class JSAutoLocalRootScope { public: JSAutoLocalRootScope(JSContext *cx) : mContext(cx) { JS_EnterLocalRootScope(mContext); } ~JSAutoLocalRootScope() { JS_LeaveLocalRootScope(mContext); } void forget(void *thing) { JS_ForgetLocalRoot(mContext, thing); } protected: JSContext *mContext; #if 0 private: static void *operator new(size_t) CPP_THROW_NEW { return 0; }; static void operator delete(void *, size_t) { }; #endif }; JS_BEGIN_EXTERN_C #endif #ifdef DEBUG extern JS_PUBLIC_API(void) JS_DumpNamedRoots(JSRuntime *rt, void (*dump)(const char *name, void *rp, void *data), void *data); #endif /* * Call JS_MapGCRoots to map the GC's roots table using map(rp, name, data). * The root is pointed at by rp; if the root is unnamed, name is null; data is * supplied from the third parameter to JS_MapGCRoots. * * The map function should return JS_MAP_GCROOT_REMOVE to cause the currently * enumerated root to be removed. To stop enumeration, set JS_MAP_GCROOT_STOP * in the return value. To keep on mapping, return JS_MAP_GCROOT_NEXT. These * constants are flags; you can OR them together. * * This function acquires and releases rt's GC lock around the mapping of the * roots table, so the map function should run to completion in as few cycles * as possible. Of course, map cannot call JS_GC, JS_MaybeGC, JS_BeginRequest, * or any JS API entry point that acquires locks, without double-tripping or * deadlocking on the GC lock. * * JS_MapGCRoots returns the count of roots that were successfully mapped. */ #define JS_MAP_GCROOT_NEXT 0 /* continue mapping entries */ #define JS_MAP_GCROOT_STOP 1 /* stop mapping entries */ #define JS_MAP_GCROOT_REMOVE 2 /* remove and free the current entry */ typedef intN (* JS_DLL_CALLBACK JSGCRootMapFun)(void *rp, const char *name, void *data); extern JS_PUBLIC_API(uint32) JS_MapGCRoots(JSRuntime *rt, JSGCRootMapFun map, void *data); extern JS_PUBLIC_API(JSBool) JS_LockGCThing(JSContext *cx, void *thing); extern JS_PUBLIC_API(JSBool) JS_LockGCThingRT(JSRuntime *rt, void *thing); extern JS_PUBLIC_API(JSBool) JS_UnlockGCThing(JSContext *cx, void *thing); extern JS_PUBLIC_API(JSBool) JS_UnlockGCThingRT(JSRuntime *rt, void *thing); /* * For implementors of JSObjectOps.mark, to mark a GC-thing reachable via a * property or other strong ref identified for debugging purposes by name. * The name argument's storage needs to live only as long as the call to * this routine. * * The final arg is used by GC_MARK_DEBUG code to build a ref path through * the GC's live thing graph. Implementors of JSObjectOps.mark should pass * its final arg through to this function when marking all GC-things that are * directly reachable from the object being marked. * * See the JSMarkOp typedef in jspubtd.h, and the JSObjectOps struct below. */ extern JS_PUBLIC_API(void) JS_MarkGCThing(JSContext *cx, void *thing, const char *name, void *arg); extern JS_PUBLIC_API(void) JS_GC(JSContext *cx); extern JS_PUBLIC_API(void) JS_MaybeGC(JSContext *cx); extern JS_PUBLIC_API(JSGCCallback) JS_SetGCCallback(JSContext *cx, JSGCCallback cb); extern JS_PUBLIC_API(JSGCCallback) JS_SetGCCallbackRT(JSRuntime *rt, JSGCCallback cb); extern JS_PUBLIC_API(JSBool) JS_IsAboutToBeFinalized(JSContext *cx, void *thing); typedef enum JSGCParamKey { JSGC_MAX_BYTES = 0, /* maximum nominal heap before last ditch GC */ JSGC_MAX_MALLOC_BYTES = 1 /* # of JS_malloc bytes before last ditch GC */ } JSGCParamKey; extern JS_PUBLIC_API(void) JS_SetGCParameter(JSRuntime *rt, JSGCParamKey key, uint32 value); /* * Add a finalizer for external strings created by JS_NewExternalString (see * below) using a type-code returned from this function, and that understands * how to free or release the memory pointed at by JS_GetStringChars(str). * * Return a nonnegative type index if there is room for finalizer in the * global GC finalizers table, else return -1. If the engine is compiled * JS_THREADSAFE and used in a multi-threaded environment, this function must * be invoked on the primordial thread only, at startup -- or else the entire * program must single-thread itself while loading a module that calls this * function. */ extern JS_PUBLIC_API(intN) JS_AddExternalStringFinalizer(JSStringFinalizeOp finalizer); /* * Remove finalizer from the global GC finalizers table, returning its type * code if found, -1 if not found. * * As with JS_AddExternalStringFinalizer, there is a threading restriction * if you compile the engine JS_THREADSAFE: this function may be called for a * given finalizer pointer on only one thread; different threads may call to * remove distinct finalizers safely. * * You must ensure that all strings with finalizer's type have been collected * before calling this function. Otherwise, string data will be leaked by the * GC, for want of a finalizer to call. */ extern JS_PUBLIC_API(intN) JS_RemoveExternalStringFinalizer(JSStringFinalizeOp finalizer); /* * Create a new JSString whose chars member refers to external memory, i.e., * memory requiring special, type-specific finalization. The type code must * be a nonnegative return value from JS_AddExternalStringFinalizer. */ extern JS_PUBLIC_API(JSString *) JS_NewExternalString(JSContext *cx, jschar *chars, size_t length, intN type); /* * Returns the external-string finalizer index for this string, or -1 if it is * an "internal" (native to JS engine) string. */ extern JS_PUBLIC_API(intN) JS_GetExternalStringGCType(JSRuntime *rt, JSString *str); /* * Sets maximum (if stack grows upward) or minimum (downward) legal stack byte * address in limitAddr for the thread or process stack used by cx. To disable * stack size checking, pass 0 for limitAddr. */ extern JS_PUBLIC_API(void) JS_SetThreadStackLimit(JSContext *cx, jsuword limitAddr); /************************************************************************/ /* * Classes, objects, and properties. */ /* For detailed comments on the function pointer types, see jspubtd.h. */ struct JSClass { const char *name; uint32 flags; /* Mandatory non-null function pointer members. */ JSPropertyOp addProperty; JSPropertyOp delProperty; JSPropertyOp getProperty; JSPropertyOp setProperty; JSEnumerateOp enumerate; JSResolveOp resolve; JSConvertOp convert; JSFinalizeOp finalize; /* Optionally non-null members start here. */ JSGetObjectOps getObjectOps; JSCheckAccessOp checkAccess; JSNative call; JSNative construct; JSXDRObjectOp xdrObject; JSHasInstanceOp hasInstance; JSMarkOp mark; JSReserveSlotsOp reserveSlots; }; struct JSExtendedClass { JSClass base; JSEqualityOp equality; JSObjectOp outerObject; JSObjectOp innerObject; void (*reserved0)(); void (*reserved1)(); void (*reserved2)(); void (*reserved3)(); void (*reserved4)(); }; #define JSCLASS_HAS_PRIVATE (1<<0) /* objects have private slot */ #define JSCLASS_NEW_ENUMERATE (1<<1) /* has JSNewEnumerateOp hook */ #define JSCLASS_NEW_RESOLVE (1<<2) /* has JSNewResolveOp hook */ #define JSCLASS_PRIVATE_IS_NSISUPPORTS (1<<3) /* private is (nsISupports *) */ #define JSCLASS_SHARE_ALL_PROPERTIES (1<<4) /* all properties are SHARED */ #define JSCLASS_NEW_RESOLVE_GETS_START (1<<5) /* JSNewResolveOp gets starting object in prototype chain passed in via *objp in/out parameter */ #define JSCLASS_CONSTRUCT_PROTOTYPE (1<<6) /* call constructor on class prototype */ #define JSCLASS_DOCUMENT_OBSERVER (1<<7) /* DOM document observer */ /* * To reserve slots fetched and stored via JS_Get/SetReservedSlot, bitwise-or * JSCLASS_HAS_RESERVED_SLOTS(n) into the initializer for JSClass.flags, where * n is a constant in [1, 255]. Reserved slots are indexed from 0 to n-1. */ #define JSCLASS_RESERVED_SLOTS_SHIFT 8 /* room for 8 flags below */ #define JSCLASS_RESERVED_SLOTS_WIDTH 8 /* and 16 above this field */ #define JSCLASS_RESERVED_SLOTS_MASK JS_BITMASK(JSCLASS_RESERVED_SLOTS_WIDTH) #define JSCLASS_HAS_RESERVED_SLOTS(n) (((n) & JSCLASS_RESERVED_SLOTS_MASK) \ << JSCLASS_RESERVED_SLOTS_SHIFT) #define JSCLASS_RESERVED_SLOTS(clasp) (((clasp)->flags \ >> JSCLASS_RESERVED_SLOTS_SHIFT) \ & JSCLASS_RESERVED_SLOTS_MASK) #define JSCLASS_HIGH_FLAGS_SHIFT (JSCLASS_RESERVED_SLOTS_SHIFT + \ JSCLASS_RESERVED_SLOTS_WIDTH) /* True if JSClass is really a JSExtendedClass. */ #define JSCLASS_IS_EXTENDED (1<<(JSCLASS_HIGH_FLAGS_SHIFT+0)) #define JSCLASS_IS_ANONYMOUS (1<<(JSCLASS_HIGH_FLAGS_SHIFT+1)) #define JSCLASS_IS_GLOBAL (1<<(JSCLASS_HIGH_FLAGS_SHIFT+2)) /* * ECMA-262 requires that most constructors used internally create objects * with "the original Foo.prototype value" as their [[Prototype]] (__proto__) * member initial value. The "original ... value" verbiage is there because * in ECMA-262, global properties naming class objects are read/write and * deleteable, for the most part. * * Implementing this efficiently requires that global objects have classes * with the following flags. Failure to use JSCLASS_GLOBAL_FLAGS won't break * anything except the ECMA-262 "original prototype value" behavior, which was * broken for years in SpiderMonkey. In other words, without these flags you * get backward compatibility. */ #define JSCLASS_GLOBAL_FLAGS \ (JSCLASS_IS_GLOBAL | JSCLASS_HAS_RESERVED_SLOTS(JSProto_LIMIT)) /* Fast access to the original value of each standard class's prototype. */ #define JSCLASS_CACHED_PROTO_SHIFT (JSCLASS_HIGH_FLAGS_SHIFT + 8) #define JSCLASS_CACHED_PROTO_WIDTH 8 #define JSCLASS_CACHED_PROTO_MASK JS_BITMASK(JSCLASS_CACHED_PROTO_WIDTH) #define JSCLASS_HAS_CACHED_PROTO(key) ((key) << JSCLASS_CACHED_PROTO_SHIFT) #define JSCLASS_CACHED_PROTO_KEY(clasp) (((clasp)->flags \ >> JSCLASS_CACHED_PROTO_SHIFT) \ & JSCLASS_CACHED_PROTO_MASK) /* Initializer for unused members of statically initialized JSClass structs. */ #define JSCLASS_NO_OPTIONAL_MEMBERS 0,0,0,0,0,0,0,0 #define JSCLASS_NO_RESERVED_MEMBERS 0,0,0,0,0 /* For detailed comments on these function pointer types, see jspubtd.h. */ struct JSObjectOps { /* Mandatory non-null function pointer members. */ JSNewObjectMapOp newObjectMap; JSObjectMapOp destroyObjectMap; JSLookupPropOp lookupProperty; JSDefinePropOp defineProperty; JSPropertyIdOp getProperty; JSPropertyIdOp setProperty; JSAttributesOp getAttributes; JSAttributesOp setAttributes; JSPropertyIdOp deleteProperty; JSConvertOp defaultValue; JSNewEnumerateOp enumerate; JSCheckAccessIdOp checkAccess; /* Optionally non-null members start here. */ JSObjectOp thisObject; JSPropertyRefOp dropProperty; JSNative call; JSNative construct; JSXDRObjectOp xdrObject; JSHasInstanceOp hasInstance; JSSetObjectSlotOp setProto; JSSetObjectSlotOp setParent; JSMarkOp mark; JSFinalizeOp clear; JSGetRequiredSlotOp getRequiredSlot; JSSetRequiredSlotOp setRequiredSlot; }; struct JSXMLObjectOps { JSObjectOps base; JSGetMethodOp getMethod; JSSetMethodOp setMethod; JSEnumerateValuesOp enumerateValues; JSEqualityOp equality; JSConcatenateOp concatenate; }; /* * Classes that expose JSObjectOps via a non-null getObjectOps class hook may * derive a property structure from this struct, return a pointer to it from * lookupProperty and defineProperty, and use the pointer to avoid rehashing * in getAttributes and setAttributes. * * The jsid type contains either an int jsval (see JSVAL_IS_INT above), or an * internal pointer that is opaque to users of this API, but which users may * convert from and to a jsval using JS_ValueToId and JS_IdToValue. */ struct JSProperty { jsid id; }; struct JSIdArray { jsint length; jsid vector[1]; /* actually, length jsid words */ }; extern JS_PUBLIC_API(void) JS_DestroyIdArray(JSContext *cx, JSIdArray *ida); extern JS_PUBLIC_API(JSBool) JS_ValueToId(JSContext *cx, jsval v, jsid *idp); extern JS_PUBLIC_API(JSBool) JS_IdToValue(JSContext *cx, jsid id, jsval *vp); /* * The magic XML namespace id is int-tagged, but not a valid integer jsval. * Global object classes in embeddings that enable JS_HAS_XML_SUPPORT (E4X) * should handle this id specially before converting id via JSVAL_TO_INT. */ #define JS_DEFAULT_XML_NAMESPACE_ID ((jsid) JSVAL_VOID) /* * JSNewResolveOp flag bits. */ #define JSRESOLVE_QUALIFIED 0x01 /* resolve a qualified property id */ #define JSRESOLVE_ASSIGNING 0x02 /* resolve on the left of assignment */ #define JSRESOLVE_DETECTING 0x04 /* 'if (o.p)...' or '(o.p) ?...:...' */ #define JSRESOLVE_DECLARING 0x08 /* var, const, or function prolog op */ #define JSRESOLVE_CLASSNAME 0x10 /* class name used when constructing */ extern JS_PUBLIC_API(JSBool) JS_PropertyStub(JSContext *cx, JSObject *obj, jsval id, jsval *vp); extern JS_PUBLIC_API(JSBool) JS_EnumerateStub(JSContext *cx, JSObject *obj); extern JS_PUBLIC_API(JSBool) JS_ResolveStub(JSContext *cx, JSObject *obj, jsval id); extern JS_PUBLIC_API(JSBool) JS_ConvertStub(JSContext *cx, JSObject *obj, JSType type, jsval *vp); extern JS_PUBLIC_API(void) JS_FinalizeStub(JSContext *cx, JSObject *obj); struct JSConstDoubleSpec { jsdouble dval; const char *name; uint8 flags; uint8 spare[3]; }; /* * To define an array element rather than a named property member, cast the * element's index to (const char *) and initialize name with it, and set the * JSPROP_INDEX bit in flags. */ struct JSPropertySpec { const char *name; int8 tinyid; uint8 flags; JSPropertyOp getter; JSPropertyOp setter; }; struct JSFunctionSpec { const char *name; JSNative call; #ifdef MOZILLA_1_8_BRANCH uint8 nargs; uint8 flags; uint16 extra; #else uint16 nargs; uint16 flags; uint32 extra; /* extra & 0xFFFF: number of arg slots for local GC roots extra >> 16: reserved, must be zero */ #endif }; extern JS_PUBLIC_API(JSObject *) JS_InitClass(JSContext *cx, JSObject *obj, JSObject *parent_proto, JSClass *clasp, JSNative constructor, uintN nargs, JSPropertySpec *ps, JSFunctionSpec *fs, JSPropertySpec *static_ps, JSFunctionSpec *static_fs); #ifdef JS_THREADSAFE extern JS_PUBLIC_API(JSClass *) JS_GetClass(JSContext *cx, JSObject *obj); #define JS_GET_CLASS(cx,obj) JS_GetClass(cx, obj) #else extern JS_PUBLIC_API(JSClass *) JS_GetClass(JSObject *obj); #define JS_GET_CLASS(cx,obj) JS_GetClass(obj) #endif extern JS_PUBLIC_API(JSBool) JS_InstanceOf(JSContext *cx, JSObject *obj, JSClass *clasp, jsval *argv); extern JS_PUBLIC_API(JSBool) JS_HasInstance(JSContext *cx, JSObject *obj, jsval v, JSBool *bp); extern JS_PUBLIC_API(void *) JS_GetPrivate(JSContext *cx, JSObject *obj); extern JS_PUBLIC_API(JSBool) JS_SetPrivate(JSContext *cx, JSObject *obj, void *data); extern JS_PUBLIC_API(void *) JS_GetInstancePrivate(JSContext *cx, JSObject *obj, JSClass *clasp, jsval *argv); extern JS_PUBLIC_API(JSObject *) JS_GetPrototype(JSContext *cx, JSObject *obj); extern JS_PUBLIC_API(JSBool) JS_SetPrototype(JSContext *cx, JSObject *obj, JSObject *proto); extern JS_PUBLIC_API(JSObject *) JS_GetParent(JSContext *cx, JSObject *obj); extern JS_PUBLIC_API(JSBool) JS_SetParent(JSContext *cx, JSObject *obj, JSObject *parent); extern JS_PUBLIC_API(JSObject *) JS_GetConstructor(JSContext *cx, JSObject *proto); /* * Get a unique identifier for obj, good for the lifetime of obj (even if it * is moved by a copying GC). Return false on failure (likely out of memory), * and true with *idp containing the unique id on success. */ extern JS_PUBLIC_API(JSBool) JS_GetObjectId(JSContext *cx, JSObject *obj, jsid *idp); extern JS_PUBLIC_API(JSObject *) JS_NewObject(JSContext *cx, JSClass *clasp, JSObject *proto, JSObject *parent); extern JS_PUBLIC_API(JSBool) JS_SealObject(JSContext *cx, JSObject *obj, JSBool deep); extern JS_PUBLIC_API(JSObject *) JS_ConstructObject(JSContext *cx, JSClass *clasp, JSObject *proto, JSObject *parent); extern JS_PUBLIC_API(JSObject *) JS_ConstructObjectWithArguments(JSContext *cx, JSClass *clasp, JSObject *proto, JSObject *parent, uintN argc, jsval *argv); extern JS_PUBLIC_API(JSObject *) JS_DefineObject(JSContext *cx, JSObject *obj, const char *name, JSClass *clasp, JSObject *proto, uintN attrs); extern JS_PUBLIC_API(JSBool) JS_DefineConstDoubles(JSContext *cx, JSObject *obj, JSConstDoubleSpec *cds); extern JS_PUBLIC_API(JSBool) JS_DefineProperties(JSContext *cx, JSObject *obj, JSPropertySpec *ps); extern JS_PUBLIC_API(JSBool) JS_DefineProperty(JSContext *cx, JSObject *obj, const char *name, jsval value, JSPropertyOp getter, JSPropertyOp setter, uintN attrs); /* * Determine the attributes (JSPROP_* flags) of a property on a given object. * * If the object does not have a property by that name, *foundp will be * JS_FALSE and the value of *attrsp is undefined. */ extern JS_PUBLIC_API(JSBool) JS_GetPropertyAttributes(JSContext *cx, JSObject *obj, const char *name, uintN *attrsp, JSBool *foundp); /* * The same, but if the property is native, return its getter and setter via * *getterp and *setterp, respectively (and only if the out parameter pointer * is not null). */ extern JS_PUBLIC_API(JSBool) JS_GetPropertyAttrsGetterAndSetter(JSContext *cx, JSObject *obj, const char *name, uintN *attrsp, JSBool *foundp, JSPropertyOp *getterp, JSPropertyOp *setterp); /* * Set the attributes of a property on a given object. * * If the object does not have a property by that name, *foundp will be * JS_FALSE and nothing will be altered. */ extern JS_PUBLIC_API(JSBool) JS_SetPropertyAttributes(JSContext *cx, JSObject *obj, const char *name, uintN attrs, JSBool *foundp); extern JS_PUBLIC_API(JSBool) JS_DefinePropertyWithTinyId(JSContext *cx, JSObject *obj, const char *name, int8 tinyid, jsval value, JSPropertyOp getter, JSPropertyOp setter, uintN attrs); extern JS_PUBLIC_API(JSBool) JS_AliasProperty(JSContext *cx, JSObject *obj, const char *name, const char *alias); extern JS_PUBLIC_API(JSBool) JS_HasProperty(JSContext *cx, JSObject *obj, const char *name, JSBool *foundp); extern JS_PUBLIC_API(JSBool) JS_LookupProperty(JSContext *cx, JSObject *obj, const char *name, jsval *vp); extern JS_PUBLIC_API(JSBool) JS_LookupPropertyWithFlags(JSContext *cx, JSObject *obj, const char *name, uintN flags, jsval *vp); extern JS_PUBLIC_API(JSBool) JS_GetProperty(JSContext *cx, JSObject *obj, const char *name, jsval *vp); extern JS_PUBLIC_API(JSBool) JS_GetMethodById(JSContext *cx, JSObject *obj, jsid id, JSObject **objp, jsval *vp); extern JS_PUBLIC_API(JSBool) JS_GetMethod(JSContext *cx, JSObject *obj, const char *name, JSObject **objp, jsval *vp); extern JS_PUBLIC_API(JSBool) JS_SetProperty(JSContext *cx, JSObject *obj, const char *name, jsval *vp); extern JS_PUBLIC_API(JSBool) JS_DeleteProperty(JSContext *cx, JSObject *obj, const char *name); extern JS_PUBLIC_API(JSBool) JS_DeleteProperty2(JSContext *cx, JSObject *obj, const char *name, jsval *rval); extern JS_PUBLIC_API(JSBool) JS_DefineUCProperty(JSContext *cx, JSObject *obj, const jschar *name, size_t namelen, jsval value, JSPropertyOp getter, JSPropertyOp setter, uintN attrs); /* * Determine the attributes (JSPROP_* flags) of a property on a given object. * * If the object does not have a property by that name, *foundp will be * JS_FALSE and the value of *attrsp is undefined. */ extern JS_PUBLIC_API(JSBool) JS_GetUCPropertyAttributes(JSContext *cx, JSObject *obj, const jschar *name, size_t namelen, uintN *attrsp, JSBool *foundp); /* * The same, but if the property is native, return its getter and setter via * *getterp and *setterp, respectively (and only if the out parameter pointer * is not null). */ extern JS_PUBLIC_API(JSBool) JS_GetUCPropertyAttrsGetterAndSetter(JSContext *cx, JSObject *obj, const jschar *name, size_t namelen, uintN *attrsp, JSBool *foundp, JSPropertyOp *getterp, JSPropertyOp *setterp); /* * Set the attributes of a property on a given object. * * If the object does not have a property by that name, *foundp will be * JS_FALSE and nothing will be altered. */ extern JS_PUBLIC_API(JSBool) JS_SetUCPropertyAttributes(JSContext *cx, JSObject *obj, const jschar *name, size_t namelen, uintN attrs, JSBool *foundp); extern JS_PUBLIC_API(JSBool) JS_DefineUCPropertyWithTinyId(JSContext *cx, JSObject *obj, const jschar *name, size_t namelen, int8 tinyid, jsval value, JSPropertyOp getter, JSPropertyOp setter, uintN attrs); extern JS_PUBLIC_API(JSBool) JS_HasUCProperty(JSContext *cx, JSObject *obj, const jschar *name, size_t namelen, JSBool *vp); extern JS_PUBLIC_API(JSBool) JS_LookupUCProperty(JSContext *cx, JSObject *obj, const jschar *name, size_t namelen, jsval *vp); extern JS_PUBLIC_API(JSBool) JS_GetUCProperty(JSContext *cx, JSObject *obj, const jschar *name, size_t namelen, jsval *vp); extern JS_PUBLIC_API(JSBool) JS_SetUCProperty(JSContext *cx, JSObject *obj, const jschar *name, size_t namelen, jsval *vp); extern JS_PUBLIC_API(JSBool) JS_DeleteUCProperty2(JSContext *cx, JSObject *obj, const jschar *name, size_t namelen, jsval *rval); extern JS_PUBLIC_API(JSObject *) JS_NewArrayObject(JSContext *cx, jsint length, jsval *vector); extern JS_PUBLIC_API(JSBool) JS_IsArrayObject(JSContext *cx, JSObject *obj); extern JS_PUBLIC_API(JSBool) JS_GetArrayLength(JSContext *cx, JSObject *obj, jsuint *lengthp); extern JS_PUBLIC_API(JSBool) JS_SetArrayLength(JSContext *cx, JSObject *obj, jsuint length); extern JS_PUBLIC_API(JSBool) JS_HasArrayLength(JSContext *cx, JSObject *obj, jsuint *lengthp); extern JS_PUBLIC_API(JSBool) JS_DefineElement(JSContext *cx, JSObject *obj, jsint index, jsval value, JSPropertyOp getter, JSPropertyOp setter, uintN attrs); extern JS_PUBLIC_API(JSBool) JS_AliasElement(JSContext *cx, JSObject *obj, const char *name, jsint alias); extern JS_PUBLIC_API(JSBool) JS_HasElement(JSContext *cx, JSObject *obj, jsint index, JSBool *foundp); extern JS_PUBLIC_API(JSBool) JS_LookupElement(JSContext *cx, JSObject *obj, jsint index, jsval *vp); extern JS_PUBLIC_API(JSBool) JS_GetElement(JSContext *cx, JSObject *obj, jsint index, jsval *vp); extern JS_PUBLIC_API(JSBool) JS_SetElement(JSContext *cx, JSObject *obj, jsint index, jsval *vp); extern JS_PUBLIC_API(JSBool) JS_DeleteElement(JSContext *cx, JSObject *obj, jsint index); extern JS_PUBLIC_API(JSBool) JS_DeleteElement2(JSContext *cx, JSObject *obj, jsint index, jsval *rval); extern JS_PUBLIC_API(void) JS_ClearScope(JSContext *cx, JSObject *obj); extern JS_PUBLIC_API(JSIdArray *) JS_Enumerate(JSContext *cx, JSObject *obj); /* * Create an object to iterate over enumerable properties of obj, in arbitrary * property definition order. NB: This differs from longstanding for..in loop * order, which uses order of property definition in obj. */ extern JS_PUBLIC_API(JSObject *) JS_NewPropertyIterator(JSContext *cx, JSObject *obj); /* * Return true on success with *idp containing the id of the next enumerable * property to visit using iterobj, or JSVAL_VOID if there is no such property * left to visit. Return false on error. */ extern JS_PUBLIC_API(JSBool) JS_NextProperty(JSContext *cx, JSObject *iterobj, jsid *idp); extern JS_PUBLIC_API(JSBool) JS_CheckAccess(JSContext *cx, JSObject *obj, jsid id, JSAccessMode mode, jsval *vp, uintN *attrsp); extern JS_PUBLIC_API(JSCheckAccessOp) JS_SetCheckObjectAccessCallback(JSRuntime *rt, JSCheckAccessOp acb); extern JS_PUBLIC_API(JSBool) JS_GetReservedSlot(JSContext *cx, JSObject *obj, uint32 index, jsval *vp); extern JS_PUBLIC_API(JSBool) JS_SetReservedSlot(JSContext *cx, JSObject *obj, uint32 index, jsval v); /************************************************************************/ /* * Security protocol. */ struct JSPrincipals { char *codebase; /* XXX unspecified and unused by Mozilla code -- can we remove these? */ void * (* JS_DLL_CALLBACK getPrincipalArray)(JSContext *cx, JSPrincipals *); JSBool (* JS_DLL_CALLBACK globalPrivilegesEnabled)(JSContext *cx, JSPrincipals *); /* Don't call "destroy"; use reference counting macros below. */ jsrefcount refcount; void (* JS_DLL_CALLBACK destroy)(JSContext *cx, JSPrincipals *); JSBool (* JS_DLL_CALLBACK subsume)(JSPrincipals *, JSPrincipals *); }; #ifdef JS_THREADSAFE #define JSPRINCIPALS_HOLD(cx, principals) JS_HoldPrincipals(cx,principals) #define JSPRINCIPALS_DROP(cx, principals) JS_DropPrincipals(cx,principals) extern JS_PUBLIC_API(jsrefcount) JS_HoldPrincipals(JSContext *cx, JSPrincipals *principals); extern JS_PUBLIC_API(jsrefcount) JS_DropPrincipals(JSContext *cx, JSPrincipals *principals); #else #define JSPRINCIPALS_HOLD(cx, principals) (++(principals)->refcount) #define JSPRINCIPALS_DROP(cx, principals) \ ((--(principals)->refcount == 0) \ ? ((*(principals)->destroy)((cx), (principals)), 0) \ : (principals)->refcount) #endif extern JS_PUBLIC_API(JSPrincipalsTranscoder) JS_SetPrincipalsTranscoder(JSRuntime *rt, JSPrincipalsTranscoder px); extern JS_PUBLIC_API(JSObjectPrincipalsFinder) JS_SetObjectPrincipalsFinder(JSRuntime *rt, JSObjectPrincipalsFinder fop); /************************************************************************/ /* * Functions and scripts. */ extern JS_PUBLIC_API(JSFunction *) JS_NewFunction(JSContext *cx, JSNative call, uintN nargs, uintN flags, JSObject *parent, const char *name); extern JS_PUBLIC_API(JSObject *) JS_GetFunctionObject(JSFunction *fun); /* * Deprecated, useful only for diagnostics. Use JS_GetFunctionId instead for * anonymous vs. "anonymous" disambiguation and Unicode fidelity. */ extern JS_PUBLIC_API(const char *) JS_GetFunctionName(JSFunction *fun); /* * Return the function's identifier as a JSString, or null if fun is unnamed. * The returned string lives as long as fun, so you don't need to root a saved * reference to it if fun is well-connected or rooted, and provided you bound * the use of the saved reference by fun's lifetime. * * Prefer JS_GetFunctionId over JS_GetFunctionName because it returns null for * truly anonymous functions, and because it doesn't chop to ISO-Latin-1 chars * from UTF-16-ish jschars. */ extern JS_PUBLIC_API(JSString *) JS_GetFunctionId(JSFunction *fun); /* * Return JSFUN_* flags for fun. */ extern JS_PUBLIC_API(uintN) JS_GetFunctionFlags(JSFunction *fun); /* * Return the arity (length) of fun. */ extern JS_PUBLIC_API(uint16) JS_GetFunctionArity(JSFunction *fun); /* * Infallible predicate to test whether obj is a function object (faster than * comparing obj's class name to "Function", but equivalent unless someone has * overwritten the "Function" identifier with a different constructor and then * created instances using that constructor that might be passed in as obj). */ extern JS_PUBLIC_API(JSBool) JS_ObjectIsFunction(JSContext *cx, JSObject *obj); extern JS_PUBLIC_API(JSBool) JS_DefineFunctions(JSContext *cx, JSObject *obj, JSFunctionSpec *fs); extern JS_PUBLIC_API(JSFunction *) JS_DefineFunction(JSContext *cx, JSObject *obj, const char *name, JSNative call, uintN nargs, uintN attrs); extern JS_PUBLIC_API(JSFunction *) JS_DefineUCFunction(JSContext *cx, JSObject *obj, const jschar *name, size_t namelen, JSNative call, uintN nargs, uintN attrs); extern JS_PUBLIC_API(JSObject *) JS_CloneFunctionObject(JSContext *cx, JSObject *funobj, JSObject *parent); /* * Given a buffer, return JS_FALSE if the buffer might become a valid * javascript statement with the addition of more lines. Otherwise return * JS_TRUE. The intent is to support interactive compilation - accumulate * lines in a buffer until JS_BufferIsCompilableUnit is true, then pass it to * the compiler. */ extern JS_PUBLIC_API(JSBool) JS_BufferIsCompilableUnit(JSContext *cx, JSObject *obj, const char *bytes, size_t length); /* * The JSScript objects returned by the following functions refer to string and * other kinds of literals, including doubles and RegExp objects. These * literals are vulnerable to garbage collection; to root script objects and * prevent literals from being collected, create a rootable object using * JS_NewScriptObject, and root the resulting object using JS_Add[Named]Root. */ extern JS_PUBLIC_API(JSScript *) JS_CompileScript(JSContext *cx, JSObject *obj, const char *bytes, size_t length, const char *filename, uintN lineno); extern JS_PUBLIC_API(JSScript *) JS_CompileScriptForPrincipals(JSContext *cx, JSObject *obj, JSPrincipals *principals, const char *bytes, size_t length, const char *filename, uintN lineno); extern JS_PUBLIC_API(JSScript *) JS_CompileUCScript(JSContext *cx, JSObject *obj, const jschar *chars, size_t length, const char *filename, uintN lineno); extern JS_PUBLIC_API(JSScript *) JS_CompileUCScriptForPrincipals(JSContext *cx, JSObject *obj, JSPrincipals *principals, const jschar *chars, size_t length, const char *filename, uintN lineno); extern JS_PUBLIC_API(JSScript *) JS_CompileFile(JSContext *cx, JSObject *obj, const char *filename); extern JS_PUBLIC_API(JSScript *) JS_CompileFileHandle(JSContext *cx, JSObject *obj, const char *filename, FILE *fh); extern JS_PUBLIC_API(JSScript *) JS_CompileFileHandleForPrincipals(JSContext *cx, JSObject *obj, const char *filename, FILE *fh, JSPrincipals *principals); /* * NB: you must use JS_NewScriptObject and root a pointer to its return value * in order to keep a JSScript and its atoms safe from garbage collection after * creating the script via JS_Compile* and before a JS_ExecuteScript* call. * E.g., and without error checks: * * JSScript *script = JS_CompileFile(cx, global, filename); * JSObject *scrobj = JS_NewScriptObject(cx, script); * JS_AddNamedRoot(cx, &scrobj, "scrobj"); * do { * jsval result; * JS_ExecuteScript(cx, global, script, &result); * JS_GC(); * } while (!JSVAL_IS_BOOLEAN(result) || JSVAL_TO_BOOLEAN(result)); * JS_RemoveRoot(cx, &scrobj); */ extern JS_PUBLIC_API(JSObject *) JS_NewScriptObject(JSContext *cx, JSScript *script); /* * Infallible getter for a script's object. If JS_NewScriptObject has not been * called on script yet, the return value will be null. */ extern JS_PUBLIC_API(JSObject *) JS_GetScriptObject(JSScript *script); extern JS_PUBLIC_API(void) JS_DestroyScript(JSContext *cx, JSScript *script); extern JS_PUBLIC_API(JSFunction *) JS_CompileFunction(JSContext *cx, JSObject *obj, const char *name, uintN nargs, const char **argnames, const char *bytes, size_t length, const char *filename, uintN lineno); extern JS_PUBLIC_API(JSFunction *) JS_CompileFunctionForPrincipals(JSContext *cx, JSObject *obj, JSPrincipals *principals, const char *name, uintN nargs, const char **argnames, const char *bytes, size_t length, const char *filename, uintN lineno); extern JS_PUBLIC_API(JSFunction *) JS_CompileUCFunction(JSContext *cx, JSObject *obj, const char *name, uintN nargs, const char **argnames, const jschar *chars, size_t length, const char *filename, uintN lineno); extern JS_PUBLIC_API(JSFunction *) JS_CompileUCFunctionForPrincipals(JSContext *cx, JSObject *obj, JSPrincipals *principals, const char *name, uintN nargs, const char **argnames, const jschar *chars, size_t length, const char *filename, uintN lineno); extern JS_PUBLIC_API(JSString *) JS_DecompileScript(JSContext *cx, JSScript *script, const char *name, uintN indent); /* * API extension: OR this into indent to avoid pretty-printing the decompiled * source resulting from JS_DecompileFunction{,Body}. */ #define JS_DONT_PRETTY_PRINT ((uintN)0x8000) extern JS_PUBLIC_API(JSString *) JS_DecompileFunction(JSContext *cx, JSFunction *fun, uintN indent); extern JS_PUBLIC_API(JSString *) JS_DecompileFunctionBody(JSContext *cx, JSFunction *fun, uintN indent); /* * NB: JS_ExecuteScript, JS_ExecuteScriptPart, and the JS_Evaluate*Script* * quadruplets all use the obj parameter as the initial scope chain header, * the 'this' keyword value, and the variables object (ECMA parlance for where * 'var' and 'function' bind names) of the execution context for script. * * Using obj as the variables object is problematic if obj's parent (which is * the scope chain link; see JS_SetParent and JS_NewObject) is not null: in * this case, variables created by 'var x = 0', e.g., go in obj, but variables * created by assignment to an unbound id, 'x = 0', go in the last object on * the scope chain linked by parent. * * ECMA calls that last scoping object the "global object", but note that many * embeddings have several such objects. ECMA requires that "global code" be * executed with the variables object equal to this global object. But these * JS API entry points provide freedom to execute code against a "sub-global", * i.e., a parented or scoped object, in which case the variables object will * differ from the last object on the scope chain, resulting in confusing and * non-ECMA explicit vs. implicit variable creation. * * Caveat embedders: unless you already depend on this buggy variables object * binding behavior, you should call JS_SetOptions(cx, JSOPTION_VAROBJFIX) or * JS_SetOptions(cx, JS_GetOptions(cx) | JSOPTION_VAROBJFIX) -- the latter if * someone may have set other options on cx already -- for each context in the * application, if you pass parented objects as the obj parameter, or may ever * pass such objects in the future. * * Why a runtime option? The alternative is to add six or so new API entry * points with signatures matching the following six, and that doesn't seem * worth the code bloat cost. Such new entry points would probably have less * obvious names, too, so would not tend to be used. The JS_SetOption call, * OTOH, can be more easily hacked into existing code that does not depend on * the bug; such code can continue to use the familiar JS_EvaluateScript, * etc., entry points. */ extern JS_PUBLIC_API(JSBool) JS_ExecuteScript(JSContext *cx, JSObject *obj, JSScript *script, jsval *rval); /* * Execute either the function-defining prolog of a script, or the script's * main body, but not both. */ typedef enum JSExecPart { JSEXEC_PROLOG, JSEXEC_MAIN } JSExecPart; extern JS_PUBLIC_API(JSBool) JS_ExecuteScriptPart(JSContext *cx, JSObject *obj, JSScript *script, JSExecPart part, jsval *rval); extern JS_PUBLIC_API(JSBool) JS_EvaluateScript(JSContext *cx, JSObject *obj, const char *bytes, uintN length, const char *filename, uintN lineno, jsval *rval); extern JS_PUBLIC_API(JSBool) JS_EvaluateScriptForPrincipals(JSContext *cx, JSObject *obj, JSPrincipals *principals, const char *bytes, uintN length, const char *filename, uintN lineno, jsval *rval); extern JS_PUBLIC_API(JSBool) JS_EvaluateUCScript(JSContext *cx, JSObject *obj, const jschar *chars, uintN length, const char *filename, uintN lineno, jsval *rval); extern JS_PUBLIC_API(JSBool) JS_EvaluateUCScriptForPrincipals(JSContext *cx, JSObject *obj, JSPrincipals *principals, const jschar *chars, uintN length, const char *filename, uintN lineno, jsval *rval); extern JS_PUBLIC_API(JSBool) JS_CallFunction(JSContext *cx, JSObject *obj, JSFunction *fun, uintN argc, jsval *argv, jsval *rval); extern JS_PUBLIC_API(JSBool) JS_CallFunctionName(JSContext *cx, JSObject *obj, const char *name, uintN argc, jsval *argv, jsval *rval); extern JS_PUBLIC_API(JSBool) JS_CallFunctionValue(JSContext *cx, JSObject *obj, jsval fval, uintN argc, jsval *argv, jsval *rval); extern JS_PUBLIC_API(JSBranchCallback) JS_SetBranchCallback(JSContext *cx, JSBranchCallback cb); extern JS_PUBLIC_API(JSBool) JS_IsRunning(JSContext *cx); extern JS_PUBLIC_API(JSBool) JS_IsConstructing(JSContext *cx); /* * Returns true if a script is executing and its current bytecode is a set * (assignment) operation, even if there are native (no script) stack frames * between the script and the caller to JS_IsAssigning. */ extern JS_FRIEND_API(JSBool) JS_IsAssigning(JSContext *cx); /* * Set the second return value, which should be a string or int jsval that * identifies a property in the returned object, to form an ECMA reference * type value (obj, id). Only native methods can return reference types, * and if the returned value is used on the left-hand side of an assignment * op, the identified property will be set. If the return value is in an * r-value, the interpreter just gets obj[id]'s value. */ extern JS_PUBLIC_API(void) JS_SetCallReturnValue2(JSContext *cx, jsval v); /* * Saving and restoring frame chains. * * These two functions are used to set aside cx->fp while that frame is * inactive. After a call to JS_SaveFrameChain, it looks as if there is no * code running on cx. Before calling JS_RestoreFrameChain, cx's call stack * must be balanced and all nested calls to JS_SaveFrameChain must have had * matching JS_RestoreFrameChain calls. * * JS_SaveFrameChain deals with cx not having any code running on it. A null * return does not signify an error and JS_RestoreFrameChain handles null * frames. */ extern JS_PUBLIC_API(JSStackFrame *) JS_SaveFrameChain(JSContext *cx); extern JS_PUBLIC_API(void) JS_RestoreFrameChain(JSContext *cx, JSStackFrame *fp); /************************************************************************/ /* * Strings. * * NB: JS_NewString takes ownership of bytes on success, avoiding a copy; but * on error (signified by null return), it leaves bytes owned by the caller. * So the caller must free bytes in the error case, if it has no use for them. * In contrast, all the JS_New*StringCopy* functions do not take ownership of * the character memory passed to them -- they copy it. */ extern JS_PUBLIC_API(JSString *) JS_NewString(JSContext *cx, char *bytes, size_t length); extern JS_PUBLIC_API(JSString *) JS_NewStringCopyN(JSContext *cx, const char *s, size_t n); extern JS_PUBLIC_API(JSString *) JS_NewStringCopyZ(JSContext *cx, const char *s); extern JS_PUBLIC_API(JSString *) JS_InternString(JSContext *cx, const char *s); extern JS_PUBLIC_API(JSString *) JS_NewUCString(JSContext *cx, jschar *chars, size_t length); extern JS_PUBLIC_API(JSString *) JS_NewUCStringCopyN(JSContext *cx, const jschar *s, size_t n); extern JS_PUBLIC_API(JSString *) JS_NewUCStringCopyZ(JSContext *cx, const jschar *s); extern JS_PUBLIC_API(JSString *) JS_InternUCStringN(JSContext *cx, const jschar *s, size_t length); extern JS_PUBLIC_API(JSString *) JS_InternUCString(JSContext *cx, const jschar *s); extern JS_PUBLIC_API(char *) JS_GetStringBytes(JSString *str); extern JS_PUBLIC_API(jschar *) JS_GetStringChars(JSString *str); extern JS_PUBLIC_API(size_t) JS_GetStringLength(JSString *str); extern JS_PUBLIC_API(intN) JS_CompareStrings(JSString *str1, JSString *str2); /* * Mutable string support. A string's characters are never mutable in this JS * implementation, but a growable string has a buffer that can be reallocated, * and a dependent string is a substring of another (growable, dependent, or * immutable) string. The direct data members of the (opaque to API clients) * JSString struct may be changed in a single-threaded way for growable and * dependent strings. * * Therefore mutable strings cannot be used by more than one thread at a time. * You may call JS_MakeStringImmutable to convert the string from a mutable * (growable or dependent) string to an immutable (and therefore thread-safe) * string. The engine takes care of converting growable and dependent strings * to immutable for you if you store strings in multi-threaded objects using * JS_SetProperty or kindred API entry points. * * If you store a JSString pointer in a native data structure that is (safely) * accessible to multiple threads, you must call JS_MakeStringImmutable before * retiring the store. */ extern JS_PUBLIC_API(JSString *) JS_NewGrowableString(JSContext *cx, jschar *chars, size_t length); /* * Create a dependent string, i.e., a string that owns no character storage, * but that refers to a slice of another string's chars. Dependent strings * are mutable by definition, so the thread safety comments above apply. */ extern JS_PUBLIC_API(JSString *) JS_NewDependentString(JSContext *cx, JSString *str, size_t start, size_t length); /* * Concatenate two strings, resulting in a new growable string. If you create * the left string and pass it to JS_ConcatStrings on a single thread, try to * use JS_NewGrowableString to create the left string -- doing so helps Concat * avoid allocating a new buffer for the result and copying left's chars into * the new buffer. See above for thread safety comments. */ extern JS_PUBLIC_API(JSString *) JS_ConcatStrings(JSContext *cx, JSString *left, JSString *right); /* * Convert a dependent string into an independent one. This function does not * change the string's mutability, so the thread safety comments above apply. */ extern JS_PUBLIC_API(const jschar *) JS_UndependString(JSContext *cx, JSString *str); /* * Convert a mutable string (either growable or dependent) into an immutable, * thread-safe one. */ extern JS_PUBLIC_API(JSBool) JS_MakeStringImmutable(JSContext *cx, JSString *str); /* * Return JS_TRUE if C (char []) strings passed via the API and internally * are UTF-8. The source must be compiled with JS_C_STRINGS_ARE_UTF8 defined * to get UTF-8 support. */ JS_PUBLIC_API(JSBool) JS_CStringsAreUTF8(); /* * Character encoding support. * * For both JS_EncodeCharacters and JS_DecodeBytes, set *dstlenp to the size * of the destination buffer before the call; on return, *dstlenp contains the * number of bytes (JS_EncodeCharacters) or jschars (JS_DecodeBytes) actually * stored. To determine the necessary destination buffer size, make a sizing * call that passes NULL for dst. * * On errors, the functions report the error. In that case, *dstlenp contains * the number of characters or bytes transferred so far. If cx is NULL, no * error is reported on failure, and the functions simply return JS_FALSE. * * NB: Neither function stores an additional zero byte or jschar after the * transcoded string. * * If the source has been compiled with the #define JS_C_STRINGS_ARE_UTF8 to * enable UTF-8 interpretation of C char[] strings, then JS_EncodeCharacters * encodes to UTF-8, and JS_DecodeBytes decodes from UTF-8, which may create * addititional errors if the character sequence is malformed. If UTF-8 * support is disabled, the functions deflate and inflate, respectively. */ JS_PUBLIC_API(JSBool) JS_EncodeCharacters(JSContext *cx, const jschar *src, size_t srclen, char *dst, size_t *dstlenp); JS_PUBLIC_API(JSBool) JS_DecodeBytes(JSContext *cx, const char *src, size_t srclen, jschar *dst, size_t *dstlenp); /************************************************************************/ /* * Locale specific string conversion and error message callbacks. */ struct JSLocaleCallbacks { JSLocaleToUpperCase localeToUpperCase; JSLocaleToLowerCase localeToLowerCase; JSLocaleCompare localeCompare; JSLocaleToUnicode localeToUnicode; JSErrorCallback localeGetErrorMessage; }; /* * Establish locale callbacks. The pointer must persist as long as the * JSContext. Passing NULL restores the default behaviour. */ extern JS_PUBLIC_API(void) JS_SetLocaleCallbacks(JSContext *cx, JSLocaleCallbacks *callbacks); /* * Return the address of the current locale callbacks struct, which may * be NULL. */ extern JS_PUBLIC_API(JSLocaleCallbacks *) JS_GetLocaleCallbacks(JSContext *cx); /************************************************************************/ /* * Error reporting. */ /* * Report an exception represented by the sprintf-like conversion of format * and its arguments. This exception message string is passed to a pre-set * JSErrorReporter function (set by JS_SetErrorReporter; see jspubtd.h for * the JSErrorReporter typedef). */ extern JS_PUBLIC_API(void) JS_ReportError(JSContext *cx, const char *format, ...); /* * Use an errorNumber to retrieve the format string, args are char * */ extern JS_PUBLIC_API(void) JS_ReportErrorNumber(JSContext *cx, JSErrorCallback errorCallback, void *userRef, const uintN errorNumber, ...); /* * Use an errorNumber to retrieve the format string, args are jschar * */ extern JS_PUBLIC_API(void) JS_ReportErrorNumberUC(JSContext *cx, JSErrorCallback errorCallback, void *userRef, const uintN errorNumber, ...); /* * As above, but report a warning instead (JSREPORT_IS_WARNING(report.flags)). * Return true if there was no error trying to issue the warning, and if the * warning was not converted into an error due to the JSOPTION_WERROR option * being set, false otherwise. */ extern JS_PUBLIC_API(JSBool) JS_ReportWarning(JSContext *cx, const char *format, ...); extern JS_PUBLIC_API(JSBool) JS_ReportErrorFlagsAndNumber(JSContext *cx, uintN flags, JSErrorCallback errorCallback, void *userRef, const uintN errorNumber, ...); extern JS_PUBLIC_API(JSBool) JS_ReportErrorFlagsAndNumberUC(JSContext *cx, uintN flags, JSErrorCallback errorCallback, void *userRef, const uintN errorNumber, ...); /* * Complain when out of memory. */ extern JS_PUBLIC_API(void) JS_ReportOutOfMemory(JSContext *cx); struct JSErrorReport { const char *filename; /* source file name, URL, etc., or null */ uintN lineno; /* source line number */ const char *linebuf; /* offending source line without final \n */ const char *tokenptr; /* pointer to error token in linebuf */ const jschar *uclinebuf; /* unicode (original) line buffer */ const jschar *uctokenptr; /* unicode (original) token pointer */ uintN flags; /* error/warning, etc. */ uintN errorNumber; /* the error number, e.g. see js.msg */ const jschar *ucmessage; /* the (default) error message */ const jschar **messageArgs; /* arguments for the error message */ }; /* * JSErrorReport flag values. These may be freely composed. */ #define JSREPORT_ERROR 0x0 /* pseudo-flag for default case */ #define JSREPORT_WARNING 0x1 /* reported via JS_ReportWarning */ #define JSREPORT_EXCEPTION 0x2 /* exception was thrown */ #define JSREPORT_STRICT 0x4 /* error or warning due to strict option */ /* * If JSREPORT_EXCEPTION is set, then a JavaScript-catchable exception * has been thrown for this runtime error, and the host should ignore it. * Exception-aware hosts should also check for JS_IsExceptionPending if * JS_ExecuteScript returns failure, and signal or propagate the exception, as * appropriate. */ #define JSREPORT_IS_WARNING(flags) (((flags) & JSREPORT_WARNING) != 0) #define JSREPORT_IS_EXCEPTION(flags) (((flags) & JSREPORT_EXCEPTION) != 0) #define JSREPORT_IS_STRICT(flags) (((flags) & JSREPORT_STRICT) != 0) extern JS_PUBLIC_API(JSErrorReporter) JS_SetErrorReporter(JSContext *cx, JSErrorReporter er); /************************************************************************/ /* * Regular Expressions. */ #define JSREG_FOLD 0x01 /* fold uppercase to lowercase */ #define JSREG_GLOB 0x02 /* global exec, creates array of matches */ #define JSREG_MULTILINE 0x04 /* treat ^ and $ as begin and end of line */ extern JS_PUBLIC_API(JSObject *) JS_NewRegExpObject(JSContext *cx, char *bytes, size_t length, uintN flags); extern JS_PUBLIC_API(JSObject *) JS_NewUCRegExpObject(JSContext *cx, jschar *chars, size_t length, uintN flags); extern JS_PUBLIC_API(void) JS_SetRegExpInput(JSContext *cx, JSString *input, JSBool multiline); extern JS_PUBLIC_API(void) JS_ClearRegExpStatics(JSContext *cx); extern JS_PUBLIC_API(void) JS_ClearRegExpRoots(JSContext *cx); /* TODO: compile, exec, get/set other statics... */ /************************************************************************/ extern JS_PUBLIC_API(JSBool) JS_IsExceptionPending(JSContext *cx); extern JS_PUBLIC_API(JSBool) JS_GetPendingException(JSContext *cx, jsval *vp); extern JS_PUBLIC_API(void) JS_SetPendingException(JSContext *cx, jsval v); extern JS_PUBLIC_API(void) JS_ClearPendingException(JSContext *cx); extern JS_PUBLIC_API(JSBool) JS_ReportPendingException(JSContext *cx); /* * Save the current exception state. This takes a snapshot of cx's current * exception state without making any change to that state. * * The returned state pointer MUST be passed later to JS_RestoreExceptionState * (to restore that saved state, overriding any more recent state) or else to * JS_DropExceptionState (to free the state struct in case it is not correct * or desirable to restore it). Both Restore and Drop free the state struct, * so callers must stop using the pointer returned from Save after calling the * Release or Drop API. */ extern JS_PUBLIC_API(JSExceptionState *) JS_SaveExceptionState(JSContext *cx); extern JS_PUBLIC_API(void) JS_RestoreExceptionState(JSContext *cx, JSExceptionState *state); extern JS_PUBLIC_API(void) JS_DropExceptionState(JSContext *cx, JSExceptionState *state); /* * If the given value is an exception object that originated from an error, * the exception will contain an error report struct, and this API will return * the address of that struct. Otherwise, it returns NULL. The lifetime of * the error report struct that might be returned is the same as the lifetime * of the exception object. */ extern JS_PUBLIC_API(JSErrorReport *) JS_ErrorFromException(JSContext *cx, jsval v); /* * Given a reported error's message and JSErrorReport struct pointer, throw * the corresponding exception on cx. */ extern JS_PUBLIC_API(JSBool) JS_ThrowReportedError(JSContext *cx, const char *message, JSErrorReport *reportp); #ifdef JS_THREADSAFE /* * Associate the current thread with the given context. This is done * implicitly by JS_NewContext. * * Returns the old thread id for this context, which should be treated as * an opaque value. This value is provided for comparison to 0, which * indicates that ClearContextThread has been called on this context * since the last SetContextThread, or non-0, which indicates the opposite. */ extern JS_PUBLIC_API(jsword) JS_GetContextThread(JSContext *cx); extern JS_PUBLIC_API(jsword) JS_SetContextThread(JSContext *cx); extern JS_PUBLIC_API(jsword) JS_ClearContextThread(JSContext *cx); #endif /* JS_THREADSAFE */ /************************************************************************/ JS_END_EXTERN_C #endif /* jsapi_h___ */ pacparser-1.4.5/src/spidermonkey/js/src/jsarena.c000066400000000000000000000407321464010763600220100ustar00rootroot00000000000000/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- * * ***** BEGIN LICENSE BLOCK ***** * Version: MPL 1.1/GPL 2.0/LGPL 2.1 * * The contents of this file are subject to the Mozilla Public License Version * 1.1 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * http://www.mozilla.org/MPL/ * * Software distributed under the License is distributed on an "AS IS" basis, * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License * for the specific language governing rights and limitations under the * License. * * The Original Code is Mozilla Communicator client code, released * March 31, 1998. * * The Initial Developer of the Original Code is * Netscape Communications Corporation. * Portions created by the Initial Developer are Copyright (C) 1998 * the Initial Developer. All Rights Reserved. * * Contributor(s): * * Alternatively, the contents of this file may be used under the terms of * either of the GNU General Public License Version 2 or later (the "GPL"), * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), * in which case the provisions of the GPL or the LGPL are applicable instead * of those above. If you wish to allow use of your version of this file only * under the terms of either the GPL or the LGPL, and not to allow others to * use your version of this file under the terms of the MPL, indicate your * decision by deleting the provisions above and replace them with the notice * and other provisions required by the GPL or the LGPL. If you do not delete * the provisions above, a recipient may use your version of this file under * the terms of any one of the MPL, the GPL or the LGPL. * * ***** END LICENSE BLOCK ***** */ /* * Lifetime-based fast allocation, inspired by much prior art, including * "Fast Allocation and Deallocation of Memory Based on Object Lifetimes" * David R. Hanson, Software -- Practice and Experience, Vol. 20(1). */ #include "jsstddef.h" #include #include #include "jstypes.h" #include "jsbit.h" #include "jsarena.h" /* Added by JSIFY */ #include "jsutil.h" /* Added by JSIFY */ #ifdef JS_ARENAMETER static JSArenaStats *arena_stats_list; #define COUNT(pool,what) (pool)->stats.what++ #else #define COUNT(pool,what) /* nothing */ #endif #define JS_ARENA_DEFAULT_ALIGN sizeof(double) JS_PUBLIC_API(void) JS_InitArenaPool(JSArenaPool *pool, const char *name, size_t size, size_t align) { if (align == 0) align = JS_ARENA_DEFAULT_ALIGN; pool->mask = JS_BITMASK(JS_CeilingLog2(align)); pool->first.next = NULL; pool->first.base = pool->first.avail = pool->first.limit = JS_ARENA_ALIGN(pool, &pool->first + 1); pool->current = &pool->first; pool->arenasize = size; #ifdef JS_ARENAMETER memset(&pool->stats, 0, sizeof pool->stats); pool->stats.name = strdup(name); pool->stats.next = arena_stats_list; arena_stats_list = &pool->stats; #endif } /* * An allocation that consumes more than pool->arenasize also has a header * pointing back to its previous arena's next member. This header is not * included in [a->base, a->limit), so its space can't be wrongly claimed. * * As the header is a pointer, it must be well-aligned. If pool->mask is * greater than or equal to POINTER_MASK, the header just preceding a->base * for an oversized arena a is well-aligned, because a->base is well-aligned. * However, we may need to add more space to pad the JSArena ** back-pointer * so that it lies just behind a->base, because a might not be aligned such * that (jsuword)(a + 1) is on a pointer boundary. * * By how much must we pad? Let M be the alignment modulus for pool and P * the modulus for a pointer. Given M >= P, the base of an oversized arena * that satisfies M is well-aligned for P. * * On the other hand, if M < P, we must include enough space in the header * size to align the back-pointer on a P boundary so that it can be found by * subtracting P from a->base. This means a->base must be on a P boundary, * even though subsequent allocations from a may be aligned on a lesser (M) * boundary. Given powers of two M and P as above, the extra space needed * when M < P is P-M or POINTER_MASK - pool->mask. * * The size of a header including padding is given by the HEADER_SIZE macro, * below, for any pool (for any value of M). * * The mask to align a->base for any pool is (pool->mask | POINTER_MASK), or * HEADER_BASE_MASK(pool). * * PTR_TO_HEADER computes the address of the back-pointer, given an oversized * allocation at p. By definition, p must be a->base for the arena a that * contains p. GET_HEADER and SET_HEADER operate on an oversized arena a, in * the case of SET_HEADER with back-pointer ap. */ #define POINTER_MASK ((jsuword)(JS_ALIGN_OF_POINTER - 1)) #define HEADER_SIZE(pool) (sizeof(JSArena **) \ + (((pool)->mask < POINTER_MASK) \ ? POINTER_MASK - (pool)->mask \ : 0)) #define HEADER_BASE_MASK(pool) ((pool)->mask | POINTER_MASK) #define PTR_TO_HEADER(pool,p) (JS_ASSERT(((jsuword)(p) \ & HEADER_BASE_MASK(pool)) \ == 0), \ (JSArena ***)(p) - 1) #define GET_HEADER(pool,a) (*PTR_TO_HEADER(pool, (a)->base)) #define SET_HEADER(pool,a,ap) (*PTR_TO_HEADER(pool, (a)->base) = (ap)) JS_PUBLIC_API(void *) JS_ArenaAllocate(JSArenaPool *pool, size_t nb) { JSArena **ap, *a, *b; jsuword extra, hdrsz, gross; void *p; /* * Search pool from current forward till we find or make enough space. * * NB: subtract nb from a->limit in the loop condition, instead of adding * nb to a->avail, to avoid overflowing a 32-bit address space (possible * when running a 32-bit program on a 64-bit system where the kernel maps * the heap up against the top of the 32-bit address space). * * Thanks to Juergen Kreileder , who brought this up in * https://bugzilla.mozilla.org/show_bug.cgi?id=279273. */ JS_ASSERT((nb & pool->mask) == 0); for (a = pool->current; nb > a->limit || a->avail > a->limit - nb; pool->current = a) { ap = &a->next; if (!*ap) { /* Not enough space in pool, so we must malloc. */ extra = (nb > pool->arenasize) ? HEADER_SIZE(pool) : 0; hdrsz = sizeof *a + extra + pool->mask; gross = hdrsz + JS_MAX(nb, pool->arenasize); if (gross < nb) return NULL; b = (JSArena *) malloc(gross); if (!b) return NULL; b->next = NULL; b->limit = (jsuword)b + gross; JS_COUNT_ARENA(pool,++); COUNT(pool, nmallocs); /* If oversized, store ap in the header, just before a->base. */ *ap = a = b; JS_ASSERT(gross <= JS_UPTRDIFF(a->limit, a)); if (extra) { a->base = a->avail = ((jsuword)a + hdrsz) & ~HEADER_BASE_MASK(pool); SET_HEADER(pool, a, ap); } else { a->base = a->avail = JS_ARENA_ALIGN(pool, a + 1); } continue; } a = *ap; /* move to next arena */ } p = (void *)a->avail; a->avail += nb; JS_ASSERT(a->base <= a->avail && a->avail <= a->limit); return p; } JS_PUBLIC_API(void *) JS_ArenaRealloc(JSArenaPool *pool, void *p, size_t size, size_t incr) { JSArena **ap, *a, *b; jsuword boff, aoff, extra, hdrsz, gross; /* * Use the oversized-single-allocation header to avoid searching for ap. * See JS_ArenaAllocate, the SET_HEADER call. */ if (size > pool->arenasize) { ap = *PTR_TO_HEADER(pool, p); a = *ap; } else { ap = &pool->first.next; while ((a = *ap) != pool->current) ap = &a->next; } JS_ASSERT(a->base == (jsuword)p); boff = JS_UPTRDIFF(a->base, a); aoff = JS_ARENA_ALIGN(pool, size + incr); JS_ASSERT(aoff > pool->arenasize); extra = HEADER_SIZE(pool); /* oversized header holds ap */ hdrsz = sizeof *a + extra + pool->mask; /* header and alignment slop */ gross = hdrsz + aoff; JS_ASSERT(gross > aoff); a = (JSArena *) realloc(a, gross); if (!a) return NULL; #ifdef JS_ARENAMETER pool->stats.nreallocs++; #endif if (a != *ap) { /* Oops, realloc moved the allocation: update other pointers to a. */ if (pool->current == *ap) pool->current = a; b = a->next; if (b && b->avail - b->base > pool->arenasize) { JS_ASSERT(GET_HEADER(pool, b) == &(*ap)->next); SET_HEADER(pool, b, &a->next); } /* Now update *ap, the next link of the arena before a. */ *ap = a; } a->base = ((jsuword)a + hdrsz) & ~HEADER_BASE_MASK(pool); a->limit = (jsuword)a + gross; a->avail = a->base + aoff; JS_ASSERT(a->base <= a->avail && a->avail <= a->limit); /* Check whether realloc aligned differently, and copy if necessary. */ if (boff != JS_UPTRDIFF(a->base, a)) memmove((void *)a->base, (char *)a + boff, size); /* Store ap in the oversized-load arena header. */ SET_HEADER(pool, a, ap); return (void *)a->base; } JS_PUBLIC_API(void *) JS_ArenaGrow(JSArenaPool *pool, void *p, size_t size, size_t incr) { void *newp; /* * If p points to an oversized allocation, it owns an entire arena, so we * can simply realloc the arena. */ if (size > pool->arenasize) return JS_ArenaRealloc(pool, p, size, incr); JS_ARENA_ALLOCATE(newp, pool, size + incr); if (newp) memcpy(newp, p, size); return newp; } /* * Free tail arenas linked after head, which may not be the true list head. * Reset pool->current to point to head in case it pointed at a tail arena. */ static void FreeArenaList(JSArenaPool *pool, JSArena *head) { JSArena **ap, *a; ap = &head->next; a = *ap; if (!a) return; #ifdef DEBUG do { JS_ASSERT(a->base <= a->avail && a->avail <= a->limit); a->avail = a->base; JS_CLEAR_UNUSED(a); } while ((a = a->next) != NULL); a = *ap; #endif do { *ap = a->next; JS_CLEAR_ARENA(a); JS_COUNT_ARENA(pool,--); free(a); } while ((a = *ap) != NULL); pool->current = head; } JS_PUBLIC_API(void) JS_ArenaRelease(JSArenaPool *pool, char *mark) { JSArena *a; for (a = &pool->first; a; a = a->next) { JS_ASSERT(a->base <= a->avail && a->avail <= a->limit); if (JS_UPTRDIFF(mark, a->base) <= JS_UPTRDIFF(a->avail, a->base)) { a->avail = JS_ARENA_ALIGN(pool, mark); JS_ASSERT(a->avail <= a->limit); FreeArenaList(pool, a); return; } } } JS_PUBLIC_API(void) JS_ArenaFreeAllocation(JSArenaPool *pool, void *p, size_t size) { JSArena **ap, *a, *b; jsuword q; /* * If the allocation is oversized, it consumes an entire arena, and it has * a header just before the allocation pointing back to its predecessor's * next member. Otherwise, we have to search pool for a. */ if (size > pool->arenasize) { ap = *PTR_TO_HEADER(pool, p); a = *ap; } else { q = (jsuword)p + size; q = JS_ARENA_ALIGN(pool, q); ap = &pool->first.next; while ((a = *ap) != NULL) { JS_ASSERT(a->base <= a->avail && a->avail <= a->limit); if (a->avail == q) { /* * If a is consumed by the allocation at p, we can free it to * the malloc heap. */ if (a->base == (jsuword)p) break; /* * We can't free a, but we can "retract" its avail cursor -- * whether there are others after it in pool. */ a->avail = (jsuword)p; return; } ap = &a->next; } } /* * At this point, a is doomed, so ensure that pool->current doesn't point * at it. We must preserve LIFO order of mark/release cursors, so we use * the oversized-allocation arena's back pointer (or if not oversized, we * use the result of searching the entire pool) to compute the address of * the arena that precedes a. */ if (pool->current == a) pool->current = (JSArena *) ((char *)ap - offsetof(JSArena, next)); /* * This is a non-LIFO deallocation, so take care to fix up a->next's back * pointer in its header, if a->next is oversized. */ *ap = b = a->next; if (b && b->avail - b->base > pool->arenasize) { JS_ASSERT(GET_HEADER(pool, b) == &a->next); SET_HEADER(pool, b, ap); } JS_CLEAR_ARENA(a); JS_COUNT_ARENA(pool,--); free(a); } JS_PUBLIC_API(void) JS_FreeArenaPool(JSArenaPool *pool) { FreeArenaList(pool, &pool->first); COUNT(pool, ndeallocs); } JS_PUBLIC_API(void) JS_FinishArenaPool(JSArenaPool *pool) { FreeArenaList(pool, &pool->first); #ifdef JS_ARENAMETER { JSArenaStats *stats, **statsp; if (pool->stats.name) free(pool->stats.name); for (statsp = &arena_stats_list; (stats = *statsp) != 0; statsp = &stats->next) { if (stats == &pool->stats) { *statsp = stats->next; return; } } } #endif } JS_PUBLIC_API(void) JS_ArenaFinish() { } JS_PUBLIC_API(void) JS_ArenaShutDown(void) { } #ifdef JS_ARENAMETER JS_PUBLIC_API(void) JS_ArenaCountAllocation(JSArenaPool *pool, size_t nb) { pool->stats.nallocs++; pool->stats.nbytes += nb; if (nb > pool->stats.maxalloc) pool->stats.maxalloc = nb; pool->stats.variance += nb * nb; } JS_PUBLIC_API(void) JS_ArenaCountInplaceGrowth(JSArenaPool *pool, size_t size, size_t incr) { pool->stats.ninplace++; } JS_PUBLIC_API(void) JS_ArenaCountGrowth(JSArenaPool *pool, size_t size, size_t incr) { pool->stats.ngrows++; pool->stats.nbytes += incr; pool->stats.variance -= size * size; size += incr; if (size > pool->stats.maxalloc) pool->stats.maxalloc = size; pool->stats.variance += size * size; } JS_PUBLIC_API(void) JS_ArenaCountRelease(JSArenaPool *pool, char *mark) { pool->stats.nreleases++; } JS_PUBLIC_API(void) JS_ArenaCountRetract(JSArenaPool *pool, char *mark) { pool->stats.nfastrels++; } #include #include JS_PUBLIC_API(void) JS_DumpArenaStats(FILE *fp) { JSArenaStats *stats; uint32 nallocs, nbytes; double mean, variance, sigma; for (stats = arena_stats_list; stats; stats = stats->next) { nallocs = stats->nallocs; if (nallocs != 0) { nbytes = stats->nbytes; mean = (double)nbytes / nallocs; variance = stats->variance * nallocs - nbytes * nbytes; if (variance < 0 || nallocs == 1) variance = 0; else variance /= nallocs * (nallocs - 1); sigma = sqrt(variance); } else { mean = variance = sigma = 0; } fprintf(fp, "\n%s allocation statistics:\n", stats->name); fprintf(fp, " number of arenas: %u\n", stats->narenas); fprintf(fp, " number of allocations: %u\n", stats->nallocs); fprintf(fp, " number of free arena reclaims: %u\n", stats->nreclaims); fprintf(fp, " number of malloc calls: %u\n", stats->nmallocs); fprintf(fp, " number of deallocations: %u\n", stats->ndeallocs); fprintf(fp, " number of allocation growths: %u\n", stats->ngrows); fprintf(fp, " number of in-place growths: %u\n", stats->ninplace); fprintf(fp, " number of realloc'ing growths: %u\n", stats->nreallocs); fprintf(fp, "number of released allocations: %u\n", stats->nreleases); fprintf(fp, " number of fast releases: %u\n", stats->nfastrels); fprintf(fp, " total bytes allocated: %u\n", stats->nbytes); fprintf(fp, " mean allocation size: %g\n", mean); fprintf(fp, " standard deviation: %g\n", sigma); fprintf(fp, " maximum allocation size: %u\n", stats->maxalloc); } } #endif /* JS_ARENAMETER */ pacparser-1.4.5/src/spidermonkey/js/src/jsarena.h000066400000000000000000000314031464010763600220100ustar00rootroot00000000000000/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- * * ***** BEGIN LICENSE BLOCK ***** * Version: MPL 1.1/GPL 2.0/LGPL 2.1 * * The contents of this file are subject to the Mozilla Public License Version * 1.1 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * http://www.mozilla.org/MPL/ * * Software distributed under the License is distributed on an "AS IS" basis, * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License * for the specific language governing rights and limitations under the * License. * * The Original Code is Mozilla Communicator client code, released * March 31, 1998. * * The Initial Developer of the Original Code is * Netscape Communications Corporation. * Portions created by the Initial Developer are Copyright (C) 1998 * the Initial Developer. All Rights Reserved. * * Contributor(s): * * Alternatively, the contents of this file may be used under the terms of * either of the GNU General Public License Version 2 or later (the "GPL"), * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), * in which case the provisions of the GPL or the LGPL are applicable instead * of those above. If you wish to allow use of your version of this file only * under the terms of either the GPL or the LGPL, and not to allow others to * use your version of this file under the terms of the MPL, indicate your * decision by deleting the provisions above and replace them with the notice * and other provisions required by the GPL or the LGPL. If you do not delete * the provisions above, a recipient may use your version of this file under * the terms of any one of the MPL, the GPL or the LGPL. * * ***** END LICENSE BLOCK ***** */ #ifndef jsarena_h___ #define jsarena_h___ /* * Lifetime-based fast allocation, inspired by much prior art, including * "Fast Allocation and Deallocation of Memory Based on Object Lifetimes" * David R. Hanson, Software -- Practice and Experience, Vol. 20(1). * * Also supports LIFO allocation (JS_ARENA_MARK/JS_ARENA_RELEASE). */ #include #include "jstypes.h" #include "jscompat.h" JS_BEGIN_EXTERN_C typedef struct JSArena JSArena; typedef struct JSArenaPool JSArenaPool; struct JSArena { JSArena *next; /* next arena for this lifetime */ jsuword base; /* aligned base address, follows this header */ jsuword limit; /* one beyond last byte in arena */ jsuword avail; /* points to next available byte */ }; #ifdef JS_ARENAMETER typedef struct JSArenaStats JSArenaStats; struct JSArenaStats { JSArenaStats *next; /* next in arenaStats list */ char *name; /* name for debugging */ uint32 narenas; /* number of arenas in pool */ uint32 nallocs; /* number of JS_ARENA_ALLOCATE() calls */ uint32 nmallocs; /* number of malloc() calls */ uint32 ndeallocs; /* number of lifetime deallocations */ uint32 ngrows; /* number of JS_ARENA_GROW() calls */ uint32 ninplace; /* number of in-place growths */ uint32 nreallocs; /* number of arena grow extending reallocs */ uint32 nreleases; /* number of JS_ARENA_RELEASE() calls */ uint32 nfastrels; /* number of "fast path" releases */ size_t nbytes; /* total bytes allocated */ size_t maxalloc; /* maximum allocation size in bytes */ double variance; /* size variance accumulator */ }; #endif struct JSArenaPool { JSArena first; /* first arena in pool list */ JSArena *current; /* arena from which to allocate space */ size_t arenasize; /* net exact size of a new arena */ jsuword mask; /* alignment mask (power-of-2 - 1) */ #ifdef JS_ARENAMETER JSArenaStats stats; #endif }; /* * If the including .c file uses only one power-of-2 alignment, it may define * JS_ARENA_CONST_ALIGN_MASK to the alignment mask and save a few instructions * per ALLOCATE and GROW. */ #ifdef JS_ARENA_CONST_ALIGN_MASK #define JS_ARENA_ALIGN(pool, n) (((jsuword)(n) + JS_ARENA_CONST_ALIGN_MASK) \ & ~(jsuword)JS_ARENA_CONST_ALIGN_MASK) #define JS_INIT_ARENA_POOL(pool, name, size) \ JS_InitArenaPool(pool, name, size, JS_ARENA_CONST_ALIGN_MASK + 1) #else #define JS_ARENA_ALIGN(pool, n) (((jsuword)(n) + (pool)->mask) & ~(pool)->mask) #endif #define JS_ARENA_ALLOCATE(p, pool, nb) \ JS_ARENA_ALLOCATE_CAST(p, void *, pool, nb) #define JS_ARENA_ALLOCATE_TYPE(p, type, pool) \ JS_ARENA_ALLOCATE_COMMON(p, type *, pool, sizeof(type), 0) #define JS_ARENA_ALLOCATE_CAST(p, type, pool, nb) \ JS_ARENA_ALLOCATE_COMMON(p, type, pool, nb, _nb > _a->limit) /* * NB: In JS_ARENA_ALLOCATE_CAST and JS_ARENA_GROW_CAST, always subtract _nb * from a->limit rather than adding _nb to _p, to avoid overflowing a 32-bit * address space (possible when running a 32-bit program on a 64-bit system * where the kernel maps the heap up against the top of the 32-bit address * space). * * Thanks to Juergen Kreileder , who brought this up in * https://bugzilla.mozilla.org/show_bug.cgi?id=279273. */ #define JS_ARENA_ALLOCATE_COMMON(p, type, pool, nb, guard) \ JS_BEGIN_MACRO \ JSArena *_a = (pool)->current; \ size_t _nb = JS_ARENA_ALIGN(pool, nb); \ jsuword _p = _a->avail; \ if ((guard) || _p > _a->limit - _nb) \ _p = (jsuword)JS_ArenaAllocate(pool, _nb); \ else \ _a->avail = _p + _nb; \ p = (type) _p; \ JS_ArenaCountAllocation(pool, nb); \ JS_END_MACRO #define JS_ARENA_GROW(p, pool, size, incr) \ JS_ARENA_GROW_CAST(p, void *, pool, size, incr) #define JS_ARENA_GROW_CAST(p, type, pool, size, incr) \ JS_BEGIN_MACRO \ JSArena *_a = (pool)->current; \ if (_a->avail == (jsuword)(p) + JS_ARENA_ALIGN(pool, size)) { \ size_t _nb = (size) + (incr); \ _nb = JS_ARENA_ALIGN(pool, _nb); \ if (_a->limit >= _nb && (jsuword)(p) <= _a->limit - _nb) { \ _a->avail = (jsuword)(p) + _nb; \ JS_ArenaCountInplaceGrowth(pool, size, incr); \ } else if ((jsuword)(p) == _a->base) { \ p = (type) JS_ArenaRealloc(pool, p, size, incr); \ } else { \ p = (type) JS_ArenaGrow(pool, p, size, incr); \ } \ } else { \ p = (type) JS_ArenaGrow(pool, p, size, incr); \ } \ JS_ArenaCountGrowth(pool, size, incr); \ JS_END_MACRO #define JS_ARENA_MARK(pool) ((void *) (pool)->current->avail) #define JS_UPTRDIFF(p,q) ((jsuword)(p) - (jsuword)(q)) #ifdef DEBUG #define JS_FREE_PATTERN 0xDA #define JS_CLEAR_UNUSED(a) (JS_ASSERT((a)->avail <= (a)->limit), \ memset((void*)(a)->avail, JS_FREE_PATTERN, \ (a)->limit - (a)->avail)) #define JS_CLEAR_ARENA(a) memset((void*)(a), JS_FREE_PATTERN, \ (a)->limit - (jsuword)(a)) #else #define JS_CLEAR_UNUSED(a) /* nothing */ #define JS_CLEAR_ARENA(a) /* nothing */ #endif #define JS_ARENA_RELEASE(pool, mark) \ JS_BEGIN_MACRO \ char *_m = (char *)(mark); \ JSArena *_a = (pool)->current; \ if (_a != &(pool)->first && \ JS_UPTRDIFF(_m, _a->base) <= JS_UPTRDIFF(_a->avail, _a->base)) { \ _a->avail = (jsuword)JS_ARENA_ALIGN(pool, _m); \ JS_ASSERT(_a->avail <= _a->limit); \ JS_CLEAR_UNUSED(_a); \ JS_ArenaCountRetract(pool, _m); \ } else { \ JS_ArenaRelease(pool, _m); \ } \ JS_ArenaCountRelease(pool, _m); \ JS_END_MACRO #ifdef JS_ARENAMETER #define JS_COUNT_ARENA(pool,op) ((pool)->stats.narenas op) #else #define JS_COUNT_ARENA(pool,op) #endif #define JS_ARENA_DESTROY(pool, a, pnext) \ JS_BEGIN_MACRO \ JS_COUNT_ARENA(pool,--); \ if ((pool)->current == (a)) (pool)->current = &(pool)->first; \ *(pnext) = (a)->next; \ JS_CLEAR_ARENA(a); \ free(a); \ (a) = NULL; \ JS_END_MACRO /* * Initialize an arena pool with the given name for debugging and metering, * with a minimum size per arena of size bytes. */ extern JS_PUBLIC_API(void) JS_InitArenaPool(JSArenaPool *pool, const char *name, size_t size, size_t align); /* * Free the arenas in pool. The user may continue to allocate from pool * after calling this function. There is no need to call JS_InitArenaPool() * again unless JS_FinishArenaPool(pool) has been called. */ extern JS_PUBLIC_API(void) JS_FreeArenaPool(JSArenaPool *pool); /* * Free the arenas in pool and finish using it altogether. */ extern JS_PUBLIC_API(void) JS_FinishArenaPool(JSArenaPool *pool); /* * Deprecated do-nothing function. */ extern JS_PUBLIC_API(void) JS_ArenaFinish(void); /* * Deprecated do-nothing function. */ extern JS_PUBLIC_API(void) JS_ArenaShutDown(void); /* * Friend functions used by the JS_ARENA_*() macros. */ extern JS_PUBLIC_API(void *) JS_ArenaAllocate(JSArenaPool *pool, size_t nb); extern JS_PUBLIC_API(void *) JS_ArenaRealloc(JSArenaPool *pool, void *p, size_t size, size_t incr); extern JS_PUBLIC_API(void *) JS_ArenaGrow(JSArenaPool *pool, void *p, size_t size, size_t incr); extern JS_PUBLIC_API(void) JS_ArenaRelease(JSArenaPool *pool, char *mark); /* * Function to be used directly when an allocation has likely grown to consume * an entire JSArena, in which case the arena is returned to the malloc heap. */ extern JS_PUBLIC_API(void) JS_ArenaFreeAllocation(JSArenaPool *pool, void *p, size_t size); #ifdef JS_ARENAMETER #include extern JS_PUBLIC_API(void) JS_ArenaCountAllocation(JSArenaPool *pool, size_t nb); extern JS_PUBLIC_API(void) JS_ArenaCountInplaceGrowth(JSArenaPool *pool, size_t size, size_t incr); extern JS_PUBLIC_API(void) JS_ArenaCountGrowth(JSArenaPool *pool, size_t size, size_t incr); extern JS_PUBLIC_API(void) JS_ArenaCountRelease(JSArenaPool *pool, char *mark); extern JS_PUBLIC_API(void) JS_ArenaCountRetract(JSArenaPool *pool, char *mark); extern JS_PUBLIC_API(void) JS_DumpArenaStats(FILE *fp); #else /* !JS_ARENAMETER */ #define JS_ArenaCountAllocation(ap, nb) /* nothing */ #define JS_ArenaCountInplaceGrowth(ap, size, incr) /* nothing */ #define JS_ArenaCountGrowth(ap, size, incr) /* nothing */ #define JS_ArenaCountRelease(ap, mark) /* nothing */ #define JS_ArenaCountRetract(ap, mark) /* nothing */ #endif /* !JS_ARENAMETER */ JS_END_EXTERN_C #endif /* jsarena_h___ */ pacparser-1.4.5/src/spidermonkey/js/src/jsarray.c000066400000000000000000001500521464010763600220350ustar00rootroot00000000000000/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- * vim: set sw=4 ts=8 et tw=80: * * ***** BEGIN LICENSE BLOCK ***** * Version: MPL 1.1/GPL 2.0/LGPL 2.1 * * The contents of this file are subject to the Mozilla Public License Version * 1.1 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * http://www.mozilla.org/MPL/ * * Software distributed under the License is distributed on an "AS IS" basis, * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License * for the specific language governing rights and limitations under the * License. * * The Original Code is Mozilla Communicator client code, released * March 31, 1998. * * The Initial Developer of the Original Code is * Netscape Communications Corporation. * Portions created by the Initial Developer are Copyright (C) 1998 * the Initial Developer. All Rights Reserved. * * Contributor(s): * * Alternatively, the contents of this file may be used under the terms of * either of the GNU General Public License Version 2 or later (the "GPL"), * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), * in which case the provisions of the GPL or the LGPL are applicable instead * of those above. If you wish to allow use of your version of this file only * under the terms of either the GPL or the LGPL, and not to allow others to * use your version of this file under the terms of the MPL, indicate your * decision by deleting the provisions above and replace them with the notice * and other provisions required by the GPL or the LGPL. If you do not delete * the provisions above, a recipient may use your version of this file under * the terms of any one of the MPL, the GPL or the LGPL. * * ***** END LICENSE BLOCK ***** */ /* * JS array class. */ #include "jsstddef.h" #include #include #include "jstypes.h" #include "jsutil.h" /* Added by JSIFY */ #include "jsapi.h" #include "jsarray.h" #include "jsatom.h" #include "jsbool.h" #include "jscntxt.h" #include "jsconfig.h" #include "jsfun.h" #include "jsgc.h" #include "jsinterp.h" #include "jslock.h" #include "jsnum.h" #include "jsobj.h" #include "jsstr.h" /* 2^32 - 1 as a number and a string */ #define MAXINDEX 4294967295u #define MAXSTR "4294967295" /* * Determine if the id represents an array index or an XML property index. * * An id is an array index according to ECMA by (15.4): * * "Array objects give special treatment to a certain class of property names. * A property name P (in the form of a string value) is an array index if and * only if ToString(ToUint32(P)) is equal to P and ToUint32(P) is not equal * to 2^32-1." * * In our implementation, it would be sufficient to check for JSVAL_IS_INT(id) * except that by using signed 32-bit integers we miss the top half of the * valid range. This function checks the string representation itself; note * that calling a standard conversion routine might allow strings such as * "08" or "4.0" as array indices, which they are not. */ JSBool js_IdIsIndex(jsval id, jsuint *indexp) { JSString *str; jschar *cp; if (JSVAL_IS_INT(id)) { jsint i; i = JSVAL_TO_INT(id); if (i < 0) return JS_FALSE; *indexp = (jsuint)i; return JS_TRUE; } /* NB: id should be a string, but jsxml.c may call us with an object id. */ if (!JSVAL_IS_STRING(id)) return JS_FALSE; str = JSVAL_TO_STRING(id); cp = JSSTRING_CHARS(str); if (JS7_ISDEC(*cp) && JSSTRING_LENGTH(str) < sizeof(MAXSTR)) { jsuint index = JS7_UNDEC(*cp++); jsuint oldIndex = 0; jsuint c = 0; if (index != 0) { while (JS7_ISDEC(*cp)) { oldIndex = index; c = JS7_UNDEC(*cp); index = 10*index + c; cp++; } } /* Ensure that all characters were consumed and we didn't overflow. */ if (*cp == 0 && (oldIndex < (MAXINDEX / 10) || (oldIndex == (MAXINDEX / 10) && c < (MAXINDEX % 10)))) { *indexp = index; return JS_TRUE; } } return JS_FALSE; } static JSBool ValueIsLength(JSContext *cx, jsval v, jsuint *lengthp) { jsint i; jsdouble d; if (JSVAL_IS_INT(v)) { i = JSVAL_TO_INT(v); if (i < 0) { JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_BAD_ARRAY_LENGTH); return JS_FALSE; } *lengthp = (jsuint) i; return JS_TRUE; } if (!js_ValueToNumber(cx, v, &d)) { JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_BAD_ARRAY_LENGTH); return JS_FALSE; } if (!js_DoubleToECMAUint32(cx, d, (uint32 *)lengthp)) { JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_BAD_ARRAY_LENGTH); return JS_FALSE; } if (JSDOUBLE_IS_NaN(d) || d != *lengthp) { JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_BAD_ARRAY_LENGTH); return JS_FALSE; } return JS_TRUE; } JSBool js_GetLengthProperty(JSContext *cx, JSObject *obj, jsuint *lengthp) { JSTempValueRooter tvr; jsid id; JSBool ok; jsint i; JS_PUSH_SINGLE_TEMP_ROOT(cx, JSVAL_NULL, &tvr); id = ATOM_TO_JSID(cx->runtime->atomState.lengthAtom); ok = OBJ_GET_PROPERTY(cx, obj, id, &tvr.u.value); if (ok) { /* * Short-circuit, because js_ValueToECMAUint32 fails when called * during init time. */ if (JSVAL_IS_INT(tvr.u.value)) { i = JSVAL_TO_INT(tvr.u.value); *lengthp = (jsuint)i; /* jsuint cast does ToUint32 */ } else { ok = js_ValueToECMAUint32(cx, tvr.u.value, (uint32 *)lengthp); } } JS_POP_TEMP_ROOT(cx, &tvr); return ok; } static JSBool IndexToValue(JSContext *cx, jsuint index, jsval *vp) { if (index <= JSVAL_INT_MAX) { *vp = INT_TO_JSVAL(index); return JS_TRUE; } return js_NewDoubleValue(cx, (jsdouble)index, vp); } static JSBool BigIndexToId(JSContext *cx, JSObject *obj, jsuint index, JSBool createAtom, jsid *idp) { jschar buf[10], *start; JSClass *clasp; JSAtom *atom; JS_STATIC_ASSERT((jsuint)-1 == 4294967295U); JS_ASSERT(index > JSVAL_INT_MAX); start = JS_ARRAY_END(buf); do { --start; *start = (jschar)('0' + index % 10); index /= 10; } while (index != 0); /* * Skip the atomization if the class is known to store atoms corresponding * to big indexes together with elements. In such case we know that the * array does not have an element at the given index if its atom does not * exist. */ if (!createAtom && ((clasp = OBJ_GET_CLASS(cx, obj)) == &js_ArrayClass || clasp == &js_ArgumentsClass || clasp == &js_ObjectClass)) { atom = js_GetExistingStringAtom(cx, start, JS_ARRAY_END(buf) - start); if (!atom) { *idp = JSVAL_VOID; return JS_TRUE; } } else { atom = js_AtomizeChars(cx, start, JS_ARRAY_END(buf) - start, 0); if (!atom) return JS_FALSE; } *idp = ATOM_TO_JSID(atom); return JS_TRUE; } /* * If the property at the given index exists, get its value into location * pointed by vp and set *hole to false. Otherwise set *hole to true and *vp * to JSVAL_VOID. This function assumes that the location pointed by vp is * properly rooted and can be used as GC-protected storage for temporaries. */ static JSBool GetArrayElement(JSContext *cx, JSObject *obj, jsuint index, JSBool *hole, jsval *vp) { jsid id; JSObject *obj2; JSProperty *prop; if (index <= JSVAL_INT_MAX) { id = INT_TO_JSID(index); } else { if (!BigIndexToId(cx, obj, index, JS_FALSE, &id)) return JS_FALSE; if (id == JSVAL_VOID) { *hole = JS_TRUE; *vp = JSVAL_VOID; return JS_TRUE; } } if (!OBJ_LOOKUP_PROPERTY(cx, obj, id, &obj2, &prop)) return JS_FALSE; if (!prop) { *hole = JS_TRUE; *vp = JSVAL_VOID; } else { OBJ_DROP_PROPERTY(cx, obj2, prop); if (!OBJ_GET_PROPERTY(cx, obj, id, vp)) return JS_FALSE; *hole = JS_FALSE; } return JS_TRUE; } /* * Set the value of the property at the given index to v assuming v is rooted. */ static JSBool SetArrayElement(JSContext *cx, JSObject *obj, jsuint index, jsval v) { jsid id; if (index <= JSVAL_INT_MAX) { id = INT_TO_JSID(index); } else { if (!BigIndexToId(cx, obj, index, JS_TRUE, &id)) return JS_FALSE; JS_ASSERT(id != JSVAL_VOID); } return OBJ_SET_PROPERTY(cx, obj, id, &v); } static JSBool DeleteArrayElement(JSContext *cx, JSObject *obj, jsuint index) { jsid id; jsval junk; if (index <= JSVAL_INT_MAX) { id = INT_TO_JSID(index); } else { if (!BigIndexToId(cx, obj, index, JS_FALSE, &id)) return JS_FALSE; if (id == JSVAL_VOID) return JS_TRUE; } return OBJ_DELETE_PROPERTY(cx, obj, id, &junk); } /* * When hole is true, delete the property at the given index. Otherwise set * its value to v assuming v is rooted. */ static JSBool SetOrDeleteArrayElement(JSContext *cx, JSObject *obj, jsuint index, JSBool hole, jsval v) { if (hole) { JS_ASSERT(v == JSVAL_VOID); return DeleteArrayElement(cx, obj, index); } else { return SetArrayElement(cx, obj, index, v); } } JSBool js_SetLengthProperty(JSContext *cx, JSObject *obj, jsuint length) { jsval v; jsid id; if (!IndexToValue(cx, length, &v)) return JS_FALSE; id = ATOM_TO_JSID(cx->runtime->atomState.lengthAtom); return OBJ_SET_PROPERTY(cx, obj, id, &v); } JSBool js_HasLengthProperty(JSContext *cx, JSObject *obj, jsuint *lengthp) { JSErrorReporter older; JSTempValueRooter tvr; jsid id; JSBool ok; older = JS_SetErrorReporter(cx, NULL); JS_PUSH_SINGLE_TEMP_ROOT(cx, JSVAL_NULL, &tvr); id = ATOM_TO_JSID(cx->runtime->atomState.lengthAtom); ok = OBJ_GET_PROPERTY(cx, obj, id, &tvr.u.value); JS_SetErrorReporter(cx, older); if (ok) ok = ValueIsLength(cx, tvr.u.value, lengthp); JS_POP_TEMP_ROOT(cx, &tvr); return ok; } JSBool js_IsArrayLike(JSContext *cx, JSObject *obj, JSBool *answerp, jsuint *lengthp) { JSClass *clasp; clasp = OBJ_GET_CLASS(cx, obj); *answerp = (clasp == &js_ArgumentsClass || clasp == &js_ArrayClass); if (!*answerp) { *lengthp = 0; return JS_TRUE; } return js_GetLengthProperty(cx, obj, lengthp); } /* * This get function is specific to Array.prototype.length and other array * instance length properties. It calls back through the class get function * in case some magic happens there (see call_getProperty in jsfun.c). */ static JSBool array_length_getter(JSContext *cx, JSObject *obj, jsval id, jsval *vp) { return OBJ_GET_CLASS(cx, obj)->getProperty(cx, obj, id, vp); } static JSBool array_length_setter(JSContext *cx, JSObject *obj, jsval id, jsval *vp) { jsuint newlen, oldlen, gap, index; jsid id2; jsval junk; JSObject *iter; JSTempValueRooter tvr; JSBool ok; if (!ValueIsLength(cx, *vp, &newlen)) return JS_FALSE; if (!js_GetLengthProperty(cx, obj, &oldlen)) return JS_FALSE; if (oldlen > newlen) { if (oldlen - newlen < (1 << 24)) { do { --oldlen; if (!DeleteArrayElement(cx, obj, oldlen)) return JS_FALSE; } while (oldlen != newlen); } else { /* * We are going to remove a lot of indexes in a presumably sparse * array. So instead of looping through indexes between newlen and * oldlen, we iterate through all properties and remove those that * correspond to indexes from the [newlen, oldlen) range. * See bug 322135. */ iter = JS_NewPropertyIterator(cx, obj); if (!iter) return JS_FALSE; /* Protect iter against GC in OBJ_DELETE_PROPERTY. */ JS_PUSH_TEMP_ROOT_OBJECT(cx, iter, &tvr); gap = oldlen - newlen; for (;;) { ok = JS_NextProperty(cx, iter, &id2); if (!ok) break; if (id2 == JSVAL_VOID) break; if (js_IdIsIndex(id2, &index) && index - newlen < gap) { ok = OBJ_DELETE_PROPERTY(cx, obj, id2, &junk); if (!ok) break; } } JS_POP_TEMP_ROOT(cx, &tvr); if (!ok) return JS_FALSE; } } return IndexToValue(cx, newlen, vp); } static JSBool array_addProperty(JSContext *cx, JSObject *obj, jsval id, jsval *vp) { jsuint index, length; if (!js_IdIsIndex(id, &index)) return JS_TRUE; if (!js_GetLengthProperty(cx, obj, &length)) return JS_FALSE; if (index >= length) { length = index + 1; return js_SetLengthProperty(cx, obj, length); } return JS_TRUE; } static JSBool array_convert(JSContext *cx, JSObject *obj, JSType type, jsval *vp) { return js_TryValueOf(cx, obj, type, vp); } JSClass js_ArrayClass = { "Array", JSCLASS_HAS_CACHED_PROTO(JSProto_Array), array_addProperty, JS_PropertyStub, JS_PropertyStub, JS_PropertyStub, JS_EnumerateStub, JS_ResolveStub, array_convert, JS_FinalizeStub, JSCLASS_NO_OPTIONAL_MEMBERS }; enum ArrayToStringOp { TO_STRING, TO_LOCALE_STRING, TO_SOURCE }; /* * When op is TO_STRING or TO_LOCALE_STRING sep indicates a separator to use * or "," when sep is NULL. * When op is TO_SOURCE sep must be NULL. */ static JSBool array_join_sub(JSContext *cx, JSObject *obj, enum ArrayToStringOp op, JSString *sep, jsval *rval) { JSBool ok, hole; jsuint length, index; jschar *chars, *ochars; size_t nchars, growth, seplen, tmplen, extratail; const jschar *sepstr; JSString *str; JSHashEntry *he; JSTempValueRooter tvr; JSAtom *atom; int stackDummy; if (!JS_CHECK_STACK_SIZE(cx, stackDummy)) { JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_OVER_RECURSED); return JS_FALSE; } ok = js_GetLengthProperty(cx, obj, &length); if (!ok) return JS_FALSE; he = js_EnterSharpObject(cx, obj, NULL, &chars); if (!he) return JS_FALSE; #ifdef DEBUG growth = (size_t) -1; #endif if (op == TO_SOURCE) { if (IS_SHARP(he)) { #if JS_HAS_SHARP_VARS nchars = js_strlen(chars); #else chars[0] = '['; chars[1] = ']'; chars[2] = 0; nchars = 2; #endif goto make_string; } /* * Always allocate 2 extra chars for closing ']' and terminating 0 * and then preallocate 1 + extratail to include starting '['. */ extratail = 2; growth = (1 + extratail) * sizeof(jschar); if (!chars) { nchars = 0; chars = (jschar *) malloc(growth); if (!chars) goto done; } else { MAKE_SHARP(he); nchars = js_strlen(chars); growth += nchars * sizeof(jschar); chars = (jschar *)realloc((ochars = chars), growth); if (!chars) { free(ochars); goto done; } } chars[nchars++] = '['; JS_ASSERT(sep == NULL); sepstr = NULL; /* indicates to use ", " as separator */ seplen = 2; } else { /* * Free any sharp variable definition in chars. Normally, we would * MAKE_SHARP(he) so that only the first sharp variable annotation is * a definition, and all the rest are references, but in the current * case of (op != TO_SOURCE), we don't need chars at all. */ if (chars) JS_free(cx, chars); chars = NULL; nchars = 0; extratail = 1; /* allocate extra char for terminating 0 */ /* Return the empty string on a cycle as well as on empty join. */ if (IS_BUSY(he) || length == 0) { js_LeaveSharpObject(cx, NULL); *rval = JS_GetEmptyStringValue(cx); return ok; } /* Flag he as BUSY so we can distinguish a cycle from a join-point. */ MAKE_BUSY(he); if (sep) { sepstr = JSSTRING_CHARS(sep); seplen = JSSTRING_LENGTH(sep); } else { sepstr = NULL; /* indicates to use "," as separator */ seplen = 1; } } /* Use rval to locally root each element value as we loop and convert. */ #define v (*rval) for (index = 0; index < length; index++) { ok = GetArrayElement(cx, obj, index, &hole, &v); if (!ok) goto done; if (hole || (op != TO_SOURCE && (JSVAL_IS_VOID(v) || JSVAL_IS_NULL(v)))) { str = cx->runtime->emptyString; } else { if (op == TO_LOCALE_STRING) { atom = cx->runtime->atomState.toLocaleStringAtom; JS_PUSH_TEMP_ROOT_OBJECT(cx, NULL, &tvr); ok = js_ValueToObject(cx, v, &tvr.u.object) && js_TryMethod(cx, tvr.u.object, atom, 0, NULL, &v); JS_POP_TEMP_ROOT(cx, &tvr); if (!ok) goto done; str = js_ValueToString(cx, v); } else if (op == TO_STRING) { str = js_ValueToString(cx, v); } else { JS_ASSERT(op == TO_SOURCE); str = js_ValueToSource(cx, v); } if (!str) { ok = JS_FALSE; goto done; } } /* * Do not append separator after the last element unless it is a hole * and we are in toSource. In that case we append single ",". */ if (index + 1 == length) seplen = (hole && op == TO_SOURCE) ? 1 : 0; /* Allocate 1 at end for closing bracket and zero. */ tmplen = JSSTRING_LENGTH(str); growth = nchars + tmplen + seplen + extratail; if (nchars > growth || tmplen > growth || growth > (size_t)-1 / sizeof(jschar)) { if (chars) { free(chars); chars = NULL; } goto done; } growth *= sizeof(jschar); if (!chars) { chars = (jschar *) malloc(growth); if (!chars) goto done; } else { chars = (jschar *) realloc((ochars = chars), growth); if (!chars) { free(ochars); goto done; } } js_strncpy(&chars[nchars], JSSTRING_CHARS(str), tmplen); nchars += tmplen; if (seplen) { if (sepstr) { js_strncpy(&chars[nchars], sepstr, seplen); } else { JS_ASSERT(seplen == 1 || seplen == 2); chars[nchars] = ','; if (seplen == 2) chars[nchars + 1] = ' '; } nchars += seplen; } } done: if (op == TO_SOURCE) { if (chars) chars[nchars++] = ']'; } else { CLEAR_BUSY(he); } js_LeaveSharpObject(cx, NULL); if (!ok) { if (chars) free(chars); return ok; } #undef v make_string: if (!chars) { JS_ReportOutOfMemory(cx); return JS_FALSE; } chars[nchars] = 0; JS_ASSERT(growth == (size_t)-1 || (nchars + 1) * sizeof(jschar) == growth); str = js_NewString(cx, chars, nchars, 0); if (!str) { free(chars); return JS_FALSE; } *rval = STRING_TO_JSVAL(str); return JS_TRUE; } #if JS_HAS_TOSOURCE static JSBool array_toSource(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) { return array_join_sub(cx, obj, TO_SOURCE, NULL, rval); } #endif static JSBool array_toString(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) { return array_join_sub(cx, obj, TO_STRING, NULL, rval); } static JSBool array_toLocaleString(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) { /* * Passing comma here as the separator. Need a way to get a * locale-specific version. */ return array_join_sub(cx, obj, TO_LOCALE_STRING, NULL, rval); } static JSBool InitArrayElements(JSContext *cx, JSObject *obj, jsuint start, jsuint end, jsval *vector) { while (start != end) { if (!SetArrayElement(cx, obj, start++, *vector++)) return JS_FALSE; } return JS_TRUE; } static JSBool InitArrayObject(JSContext *cx, JSObject *obj, jsuint length, jsval *vector) { jsval v; jsid id; if (!IndexToValue(cx, length, &v)) return JS_FALSE; id = ATOM_TO_JSID(cx->runtime->atomState.lengthAtom); if (!OBJ_DEFINE_PROPERTY(cx, obj, id, v, array_length_getter, array_length_setter, JSPROP_PERMANENT, NULL)) { return JS_FALSE; } if (!vector) return JS_TRUE; return InitArrayElements(cx, obj, 0, length, vector); } /* * Perl-inspired join, reverse, and sort. */ static JSBool array_join(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) { JSString *str; if (JSVAL_IS_VOID(argv[0])) { str = NULL; } else { str = js_ValueToString(cx, argv[0]); if (!str) return JS_FALSE; argv[0] = STRING_TO_JSVAL(str); } return array_join_sub(cx, obj, TO_STRING, str, rval); } static JSBool array_reverse(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) { jsuint len, half, i; JSBool hole, hole2; jsval *tmproot, *tmproot2; if (!js_GetLengthProperty(cx, obj, &len)) return JS_FALSE; /* * Use argv[argc] and argv[argc + 1] as local roots to hold temporarily * array elements for GC-safe swap. */ tmproot = argv + argc; tmproot2 = argv + argc + 1; half = len / 2; for (i = 0; i < half; i++) { if (!GetArrayElement(cx, obj, i, &hole, tmproot) || !GetArrayElement(cx, obj, len - i - 1, &hole2, tmproot2) || !SetOrDeleteArrayElement(cx, obj, len - i - 1, hole, *tmproot) || !SetOrDeleteArrayElement(cx, obj, i, hole2, *tmproot2)) { return JS_FALSE; } } *rval = OBJECT_TO_JSVAL(obj); return JS_TRUE; } typedef struct HSortArgs { void *vec; size_t elsize; void *pivot; JSComparator cmp; void *arg; JSBool fastcopy; } HSortArgs; static JSBool sort_compare(void *arg, const void *a, const void *b, int *result); static int sort_compare_strings(void *arg, const void *a, const void *b, int *result); static JSBool HeapSortHelper(JSBool building, HSortArgs *hsa, size_t lo, size_t hi) { void *pivot, *vec, *vec2, *arg, *a, *b; size_t elsize; JSComparator cmp; JSBool fastcopy; size_t j, hiDiv2; int cmp_result; pivot = hsa->pivot; vec = hsa->vec; elsize = hsa->elsize; vec2 = (char *)vec - 2 * elsize; cmp = hsa->cmp; arg = hsa->arg; fastcopy = hsa->fastcopy; #define MEMCPY(p,q,n) \ (fastcopy ? (void)(*(jsval*)(p) = *(jsval*)(q)) : (void)memcpy(p, q, n)) #define CALL_CMP(a, b) \ if (!cmp(arg, (a), (b), &cmp_result)) return JS_FALSE; if (lo == 1) { j = 2; b = (char *)vec + elsize; if (j < hi) { CALL_CMP(vec, b); if (cmp_result < 0) j++; } a = (char *)vec + (hi - 1) * elsize; b = (char *)vec2 + j * elsize; /* * During sorting phase b points to a member of heap that cannot be * bigger then biggest of vec[0] and vec[1], and cmp(a, b, arg) <= 0 * always holds. */ if (building || hi == 2) { CALL_CMP(a, b); if (cmp_result >= 0) return JS_TRUE; } MEMCPY(pivot, a, elsize); MEMCPY(a, b, elsize); lo = j; } else { a = (char *)vec2 + lo * elsize; MEMCPY(pivot, a, elsize); } hiDiv2 = hi/2; while (lo <= hiDiv2) { j = lo + lo; a = (char *)vec2 + j * elsize; b = (char *)vec + (j - 1) * elsize; if (j < hi) { CALL_CMP(a, b); if (cmp_result < 0) j++; } b = (char *)vec2 + j * elsize; CALL_CMP(pivot, b); if (cmp_result >= 0) break; a = (char *)vec2 + lo * elsize; MEMCPY(a, b, elsize); lo = j; } a = (char *)vec2 + lo * elsize; MEMCPY(a, pivot, elsize); return JS_TRUE; #undef CALL_CMP #undef MEMCPY } JSBool js_HeapSort(void *vec, size_t nel, void *pivot, size_t elsize, JSComparator cmp, void *arg) { HSortArgs hsa; size_t i; hsa.vec = vec; hsa.elsize = elsize; hsa.pivot = pivot; hsa.cmp = cmp; hsa.arg = arg; hsa.fastcopy = (cmp == sort_compare || cmp == sort_compare_strings); for (i = nel/2; i != 0; i--) { if (!HeapSortHelper(JS_TRUE, &hsa, i, nel)) return JS_FALSE; } while (nel > 2) { if (!HeapSortHelper(JS_FALSE, &hsa, 1, --nel)) return JS_FALSE; } return JS_TRUE; } typedef struct CompareArgs { JSContext *context; jsval fval; jsval *localroot; /* need one local root, for sort_compare */ } CompareArgs; static JSBool sort_compare(void *arg, const void *a, const void *b, int *result) { jsval av = *(const jsval *)a, bv = *(const jsval *)b; CompareArgs *ca = (CompareArgs *) arg; JSContext *cx = ca->context; jsval fval; JSBool ok; /** * array_sort deals with holes and undefs on its own and they should not * come here. */ JS_ASSERT(av != JSVAL_VOID); JS_ASSERT(bv != JSVAL_VOID); *result = 0; ok = JS_TRUE; fval = ca->fval; if (fval == JSVAL_NULL) { JSString *astr, *bstr; if (av != bv) { /* * Set our local root to astr in case the second js_ValueToString * displaces the newborn root in cx, and the GC nests under that * call. Don't bother guarding the local root store with an astr * non-null test. If we tag null as a string, the GC will untag, * null-test, and avoid dereferencing null. */ astr = js_ValueToString(cx, av); *ca->localroot = STRING_TO_JSVAL(astr); if (astr && (bstr = js_ValueToString(cx, bv))) *result = js_CompareStrings(astr, bstr); else ok = JS_FALSE; } } else { jsdouble cmp; jsval argv[2]; argv[0] = av; argv[1] = bv; ok = js_InternalCall(cx, OBJ_GET_PARENT(cx, JSVAL_TO_OBJECT(fval)), fval, 2, argv, ca->localroot); if (ok) { ok = js_ValueToNumber(cx, *ca->localroot, &cmp); /* Clamp cmp to -1, 0, 1. */ if (ok) { if (JSDOUBLE_IS_NaN(cmp)) { /* * XXX report some kind of error here? ECMA talks about * 'consistent compare functions' that don't return NaN, * but is silent about what the result should be. So we * currently ignore it. */ } else if (cmp != 0) { *result = cmp > 0 ? 1 : -1; } } } } return ok; } static int sort_compare_strings(void *arg, const void *a, const void *b, int *result) { jsval av = *(const jsval *)a, bv = *(const jsval *)b; *result = (int) js_CompareStrings(JSVAL_TO_STRING(av), JSVAL_TO_STRING(bv)); return JS_TRUE; } static JSBool array_sort(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) { jsval fval, *vec, *pivotroot; CompareArgs ca; jsuint len, newlen, i, undefs; JSTempValueRooter tvr; JSBool hole, ok; /* * Optimize the default compare function case if all of obj's elements * have values of type string. */ JSBool all_strings; if (argc > 0) { if (JSVAL_IS_PRIMITIVE(argv[0])) { JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_BAD_SORT_ARG); return JS_FALSE; } fval = argv[0]; all_strings = JS_FALSE; /* non-default compare function */ } else { fval = JSVAL_NULL; all_strings = JS_TRUE; /* check for all string values */ } if (!js_GetLengthProperty(cx, obj, &len)) return JS_FALSE; if (len == 0) { *rval = OBJECT_TO_JSVAL(obj); return JS_TRUE; } /* * We need a temporary array of len jsvals to hold elements of the array. * Check that its size does not overflow size_t, which would allow for * indexing beyond the end of the malloc'd vector. */ if (len > ((size_t) -1) / sizeof(jsval)) { JS_ReportOutOfMemory(cx); return JS_FALSE; } vec = (jsval *) JS_malloc(cx, ((size_t) len) * sizeof(jsval)); if (!vec) return JS_FALSE; /* * Initialize vec as a root. We will clear elements of vec one by * one while increasing tvr.count when we know that the property at * the corresponding index exists and its value must be rooted. * * In this way when sorting a huge mostly sparse array we will not * access the tail of vec corresponding to properties that do not * exist, allowing OS to avoiding committing RAM. See bug 330812. * * After this point control must flow through label out: to exit. */ JS_PUSH_TEMP_ROOT(cx, 0, vec, &tvr); /* * By ECMA 262, 15.4.4.11, a property that does not exist (which we * call a "hole") is always greater than an existing property with * value undefined and that is always greater than any other property. * Thus to sort holes and undefs we simply count them, sort the rest * of elements, append undefs after them and then make holes after * undefs. */ undefs = 0; newlen = 0; for (i = 0; i < len; i++) { /* Clear vec[newlen] before including it in the rooted set. */ vec[newlen] = JSVAL_NULL; tvr.count = newlen + 1; ok = GetArrayElement(cx, obj, i, &hole, &vec[newlen]); if (!ok) goto out; if (hole) continue; if (vec[newlen] == JSVAL_VOID) { ++undefs; continue; } /* We know JSVAL_IS_STRING yields 0 or 1, so avoid a branch via &=. */ all_strings &= JSVAL_IS_STRING(vec[newlen]); ++newlen; } /* Here len == newlen + undefs + number_of_holes. */ ca.context = cx; ca.fval = fval; ca.localroot = argv + argc; /* local GC root for temporary string */ pivotroot = argv + argc + 1; /* local GC root for pivot val */ ok = js_HeapSort(vec, (size_t) newlen, pivotroot, sizeof(jsval), all_strings ? sort_compare_strings : sort_compare, &ca); if (!ok) goto out; ok = InitArrayElements(cx, obj, 0, newlen, vec); if (!ok) goto out; out: JS_POP_TEMP_ROOT(cx, &tvr); JS_free(cx, vec); if (!ok) return JS_FALSE; /* Set undefs that sorted after the rest of elements. */ while (undefs != 0) { --undefs; if (!SetArrayElement(cx, obj, newlen++, JSVAL_VOID)) return JS_FALSE; } /* Re-create any holes that sorted to the end of the array. */ while (len > newlen) { if (!DeleteArrayElement(cx, obj, --len)) return JS_FALSE; } *rval = OBJECT_TO_JSVAL(obj); return JS_TRUE; } /* * Perl-inspired push, pop, shift, unshift, and splice methods. */ static JSBool array_push(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) { jsuint length, newlength; if (!js_GetLengthProperty(cx, obj, &length)) return JS_FALSE; newlength = length + argc; if (!InitArrayElements(cx, obj, length, newlength, argv)) return JS_FALSE; /* Per ECMA-262, return the new array length. */ if (!IndexToValue(cx, newlength, rval)) return JS_FALSE; return js_SetLengthProperty(cx, obj, newlength); } static JSBool array_pop(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) { jsuint index; JSBool hole; if (!js_GetLengthProperty(cx, obj, &index)) return JS_FALSE; if (index > 0) { index--; /* Get the to-be-deleted property's value into rval. */ if (!GetArrayElement(cx, obj, index, &hole, rval)) return JS_FALSE; if (!hole && !DeleteArrayElement(cx, obj, index)) return JS_FALSE; } return js_SetLengthProperty(cx, obj, index); } static JSBool array_shift(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) { jsuint length, i; JSBool hole; if (!js_GetLengthProperty(cx, obj, &length)) return JS_FALSE; if (length == 0) { *rval = JSVAL_VOID; } else { length--; /* Get the to-be-deleted property's value into rval ASAP. */ if (!GetArrayElement(cx, obj, 0, &hole, rval)) return JS_FALSE; /* * Slide down the array above the first element. */ for (i = 0; i != length; i++) { if (!GetArrayElement(cx, obj, i + 1, &hole, &argv[0])) return JS_FALSE; if (!SetOrDeleteArrayElement(cx, obj, i, hole, argv[0])) return JS_FALSE; } /* Delete the only or last element when it exist. */ if (!hole && !DeleteArrayElement(cx, obj, length)) return JS_FALSE; } return js_SetLengthProperty(cx, obj, length); } static JSBool array_unshift(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) { jsuint length, last; jsval *vp; JSBool hole; if (!js_GetLengthProperty(cx, obj, &length)) return JS_FALSE; if (argc > 0) { /* Slide up the array to make room for argc at the bottom. */ if (length > 0) { last = length; vp = argv + argc; /* local root */ do { --last; if (!GetArrayElement(cx, obj, last, &hole, vp) || !SetOrDeleteArrayElement(cx, obj, last + argc, hole, *vp)) { return JS_FALSE; } } while (last != 0); } /* Copy from argv to the bottom of the array. */ if (!InitArrayElements(cx, obj, 0, argc, argv)) return JS_FALSE; length += argc; if (!js_SetLengthProperty(cx, obj, length)) return JS_FALSE; } /* Follow Perl by returning the new array length. */ return IndexToValue(cx, length, rval); } static JSBool array_splice(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) { jsval *vp; jsuint length, begin, end, count, delta, last; jsdouble d; JSBool hole; JSObject *obj2; /* * Nothing to do if no args. Otherwise point vp at our one explicit local * root and get length. */ if (argc == 0) return JS_TRUE; vp = argv + argc; if (!js_GetLengthProperty(cx, obj, &length)) return JS_FALSE; /* Convert the first argument into a starting index. */ if (!js_ValueToNumber(cx, *argv, &d)) return JS_FALSE; d = js_DoubleToInteger(d); if (d < 0) { d += length; if (d < 0) d = 0; } else if (d > length) { d = length; } begin = (jsuint)d; /* d has been clamped to uint32 */ argc--; argv++; /* Convert the second argument from a count into a fencepost index. */ delta = length - begin; if (argc == 0) { count = delta; end = length; } else { if (!js_ValueToNumber(cx, *argv, &d)) return JS_FALSE; d = js_DoubleToInteger(d); if (d < 0) d = 0; else if (d > delta) d = delta; count = (jsuint)d; end = begin + count; argc--; argv++; } /* * Create a new array value to return. Our ECMA v2 proposal specs * that splice always returns an array value, even when given no * arguments. We think this is best because it eliminates the need * for callers to do an extra test to handle the empty splice case. */ obj2 = js_NewArrayObject(cx, 0, NULL); if (!obj2) return JS_FALSE; *rval = OBJECT_TO_JSVAL(obj2); /* If there are elements to remove, put them into the return value. */ if (count > 0) { for (last = begin; last < end; last++) { if (!GetArrayElement(cx, obj, last, &hole, vp)) return JS_FALSE; /* Copy *vp to new array unless it's a hole. */ if (!hole && !SetArrayElement(cx, obj2, last - begin, *vp)) return JS_FALSE; } if (!js_SetLengthProperty(cx, obj2, end - begin)) return JS_FALSE; } /* Find the direction (up or down) to copy and make way for argv. */ if (argc > count) { delta = (jsuint)argc - count; last = length; /* (uint) end could be 0, so can't use vanilla >= test */ while (last-- > end) { if (!GetArrayElement(cx, obj, last, &hole, vp) || !SetOrDeleteArrayElement(cx, obj, last + delta, hole, *vp)) { return JS_FALSE; } } length += delta; } else if (argc < count) { delta = count - (jsuint)argc; for (last = end; last < length; last++) { if (!GetArrayElement(cx, obj, last, &hole, vp) || !SetOrDeleteArrayElement(cx, obj, last - delta, hole, *vp)) { return JS_FALSE; } } length -= delta; } /* Copy from argv into the hole to complete the splice. */ if (!InitArrayElements(cx, obj, begin, begin + argc, argv)) return JS_FALSE; /* Update length in case we deleted elements from the end. */ return js_SetLengthProperty(cx, obj, length); } /* * Python-esque sequence operations. */ static JSBool array_concat(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) { jsval *vp, v; JSObject *nobj, *aobj; jsuint length, alength, slot; uintN i; JSBool hole; /* Hoist the explicit local root address computation. */ vp = argv + argc; /* Treat obj as the first argument; see ECMA 15.4.4.4. */ --argv; JS_ASSERT(obj == JSVAL_TO_OBJECT(argv[0])); /* Create a new Array object and store it in the rval local root. */ nobj = js_NewArrayObject(cx, 0, NULL); if (!nobj) return JS_FALSE; *rval = OBJECT_TO_JSVAL(nobj); /* Loop over [0, argc] to concat args into nobj, expanding all Arrays. */ length = 0; for (i = 0; i <= argc; i++) { v = argv[i]; if (JSVAL_IS_OBJECT(v)) { aobj = JSVAL_TO_OBJECT(v); if (aobj && OBJ_GET_CLASS(cx, aobj) == &js_ArrayClass) { if (!OBJ_GET_PROPERTY(cx, aobj, ATOM_TO_JSID(cx->runtime->atomState .lengthAtom), vp)) { return JS_FALSE; } if (!ValueIsLength(cx, *vp, &alength)) return JS_FALSE; for (slot = 0; slot < alength; slot++) { if (!GetArrayElement(cx, aobj, slot, &hole, vp)) return JS_FALSE; /* * Per ECMA 262, 15.4.4.4, step 9, ignore non-existent * properties. */ if (!hole && !SetArrayElement(cx, nobj, length + slot, *vp)) return JS_FALSE; } length += alength; continue; } } if (!SetArrayElement(cx, nobj, length, v)) return JS_FALSE; length++; } return js_SetLengthProperty(cx, nobj, length); } static JSBool array_slice(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) { jsval *vp; JSObject *nobj; jsuint length, begin, end, slot; jsdouble d; JSBool hole; /* Hoist the explicit local root address computation. */ vp = argv + argc; /* Create a new Array object and store it in the rval local root. */ nobj = js_NewArrayObject(cx, 0, NULL); if (!nobj) return JS_FALSE; *rval = OBJECT_TO_JSVAL(nobj); if (!js_GetLengthProperty(cx, obj, &length)) return JS_FALSE; begin = 0; end = length; if (argc > 0) { if (!js_ValueToNumber(cx, argv[0], &d)) return JS_FALSE; d = js_DoubleToInteger(d); if (d < 0) { d += length; if (d < 0) d = 0; } else if (d > length) { d = length; } begin = (jsuint)d; if (argc > 1) { if (!js_ValueToNumber(cx, argv[1], &d)) return JS_FALSE; d = js_DoubleToInteger(d); if (d < 0) { d += length; if (d < 0) d = 0; } else if (d > length) { d = length; } end = (jsuint)d; } } if (begin > end) begin = end; for (slot = begin; slot < end; slot++) { if (!GetArrayElement(cx, obj, slot, &hole, vp)) return JS_FALSE; if (!hole && !SetArrayElement(cx, nobj, slot - begin, *vp)) return JS_FALSE; } return js_SetLengthProperty(cx, nobj, end - begin); } #if JS_HAS_ARRAY_EXTRAS static JSBool array_indexOfHelper(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval, JSBool isLast) { jsuint length, i, stop; jsint direction; JSBool hole; if (!js_GetLengthProperty(cx, obj, &length)) return JS_FALSE; if (length == 0) goto not_found; if (argc <= 1) { i = isLast ? length - 1 : 0; } else { jsdouble start; if (!js_ValueToNumber(cx, argv[1], &start)) return JS_FALSE; start = js_DoubleToInteger(start); if (start < 0) { start += length; if (start < 0) { if (isLast) goto not_found; i = 0; } else { i = (jsuint)start; } } else if (start >= length) { if (!isLast) goto not_found; i = length - 1; } else { i = (jsuint)start; } } if (isLast) { stop = 0; direction = -1; } else { stop = length - 1; direction = 1; } for (;;) { if (!GetArrayElement(cx, obj, (jsuint)i, &hole, rval)) return JS_FALSE; if (!hole && js_StrictlyEqual(*rval, argv[0])) return js_NewNumberValue(cx, i, rval); if (i == stop) goto not_found; i += direction; } not_found: *rval = INT_TO_JSVAL(-1); return JS_TRUE; } static JSBool array_indexOf(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) { return array_indexOfHelper(cx, obj, argc, argv, rval, JS_FALSE); } static JSBool array_lastIndexOf(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) { return array_indexOfHelper(cx, obj, argc, argv, rval, JS_TRUE); } /* Order is important; extras that use a caller's predicate must follow MAP. */ typedef enum ArrayExtraMode { FOREACH, MAP, FILTER, SOME, EVERY } ArrayExtraMode; static JSBool array_extra(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval, ArrayExtraMode mode) { jsval *vp, *sp, *origsp, *oldsp; jsuint length, newlen, i; JSObject *callable, *thisp, *newarr; void *mark; JSStackFrame *fp; JSBool ok, cond, hole; /* Hoist the explicit local root address computation. */ vp = argv + argc; if (!js_GetLengthProperty(cx, obj, &length)) return JS_FALSE; /* * First, get or compute our callee, so that we error out consistently * when passed a non-callable object. */ callable = js_ValueToCallableObject(cx, &argv[0], JSV2F_SEARCH_STACK); if (!callable) return JS_FALSE; /* * Set our initial return condition, used for zero-length array cases * (and pre-size our map return to match our known length, for all cases). */ #ifdef __GNUC__ /* quell GCC overwarning */ newlen = 0; newarr = NULL; ok = JS_TRUE; #endif switch (mode) { case MAP: case FILTER: newlen = (mode == MAP) ? length : 0; newarr = js_NewArrayObject(cx, newlen, NULL); if (!newarr) return JS_FALSE; *rval = OBJECT_TO_JSVAL(newarr); break; case SOME: *rval = JSVAL_FALSE; break; case EVERY: *rval = JSVAL_TRUE; break; case FOREACH: break; } if (length == 0) return JS_TRUE; if (argc > 1) { if (!js_ValueToObject(cx, argv[1], &thisp)) return JS_FALSE; argv[1] = OBJECT_TO_JSVAL(thisp); } else { thisp = NULL; } /* We call with 3 args (value, index, array), plus room for rval. */ origsp = js_AllocStack(cx, 2 + 3 + 1, &mark); if (!origsp) return JS_FALSE; /* Lift current frame to include our args. */ fp = cx->fp; oldsp = fp->sp; for (i = 0; i < length; i++) { ok = GetArrayElement(cx, obj, i, &hole, vp); if (!ok) break; if (hole) continue; /* * Push callable and 'this', then args. We must do this for every * iteration around the loop since js_Invoke uses origsp[0] for rval * storage and some native functions use origsp[1] for local rooting. */ sp = origsp; *sp++ = OBJECT_TO_JSVAL(callable); *sp++ = OBJECT_TO_JSVAL(thisp); *sp++ = *vp; *sp++ = INT_TO_JSVAL(i); *sp++ = OBJECT_TO_JSVAL(obj); /* Do the call. */ fp->sp = sp; ok = js_Invoke(cx, 3, JSINVOKE_INTERNAL); vp[1] = fp->sp[-1]; fp->sp = oldsp; if (!ok) break; if (mode > MAP) { if (vp[1] == JSVAL_NULL) { cond = JS_FALSE; } else if (JSVAL_IS_BOOLEAN(vp[1])) { cond = JSVAL_TO_BOOLEAN(vp[1]); } else { ok = js_ValueToBoolean(cx, vp[1], &cond); if (!ok) goto out; } } switch (mode) { case FOREACH: break; case MAP: ok = SetArrayElement(cx, newarr, i, vp[1]); if (!ok) goto out; break; case FILTER: if (!cond) break; /* Filter passed *vp, push as result. */ ok = SetArrayElement(cx, newarr, newlen++, *vp); if (!ok) goto out; break; case SOME: if (cond) { *rval = JSVAL_TRUE; goto out; } break; case EVERY: if (!cond) { *rval = JSVAL_FALSE; goto out; } break; } } out: js_FreeStack(cx, mark); if (ok && mode == FILTER) ok = js_SetLengthProperty(cx, newarr, newlen); return ok; } static JSBool array_forEach(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) { return array_extra(cx, obj, argc, argv, rval, FOREACH); } static JSBool array_map(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) { return array_extra(cx, obj, argc, argv, rval, MAP); } static JSBool array_filter(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) { return array_extra(cx, obj, argc, argv, rval, FILTER); } static JSBool array_some(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) { return array_extra(cx, obj, argc, argv, rval, SOME); } static JSBool array_every(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) { return array_extra(cx, obj, argc, argv, rval, EVERY); } #endif static JSFunctionSpec array_methods[] = { #if JS_HAS_TOSOURCE {js_toSource_str, array_toSource, 0,0,0}, #endif {js_toString_str, array_toString, 0,0,0}, {js_toLocaleString_str, array_toLocaleString, 0,0,0}, /* Perl-ish methods. */ {"join", array_join, 1,JSFUN_GENERIC_NATIVE,0}, {"reverse", array_reverse, 0,JSFUN_GENERIC_NATIVE,2}, {"sort", array_sort, 1,JSFUN_GENERIC_NATIVE,2}, {"push", array_push, 1,JSFUN_GENERIC_NATIVE,0}, {"pop", array_pop, 0,JSFUN_GENERIC_NATIVE,0}, {"shift", array_shift, 0,JSFUN_GENERIC_NATIVE,1}, {"unshift", array_unshift, 1,JSFUN_GENERIC_NATIVE,1}, {"splice", array_splice, 2,JSFUN_GENERIC_NATIVE,1}, /* Python-esque sequence methods. */ {"concat", array_concat, 1,JSFUN_GENERIC_NATIVE,1}, {"slice", array_slice, 2,JSFUN_GENERIC_NATIVE,1}, #if JS_HAS_ARRAY_EXTRAS {"indexOf", array_indexOf, 1,JSFUN_GENERIC_NATIVE,0}, {"lastIndexOf", array_lastIndexOf, 1,JSFUN_GENERIC_NATIVE,0}, {"forEach", array_forEach, 1,JSFUN_GENERIC_NATIVE,2}, {"map", array_map, 1,JSFUN_GENERIC_NATIVE,2}, {"filter", array_filter, 1,JSFUN_GENERIC_NATIVE,2}, {"some", array_some, 1,JSFUN_GENERIC_NATIVE,2}, {"every", array_every, 1,JSFUN_GENERIC_NATIVE,2}, #endif {0,0,0,0,0} }; static JSBool Array(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) { jsuint length; jsval *vector; /* If called without new, replace obj with a new Array object. */ if (!(cx->fp->flags & JSFRAME_CONSTRUCTING)) { obj = js_NewObject(cx, &js_ArrayClass, NULL, NULL); if (!obj) return JS_FALSE; *rval = OBJECT_TO_JSVAL(obj); } if (argc == 0) { length = 0; vector = NULL; } else if (argc > 1) { length = (jsuint) argc; vector = argv; } else if (!JSVAL_IS_NUMBER(argv[0])) { length = 1; vector = argv; } else { if (!ValueIsLength(cx, argv[0], &length)) return JS_FALSE; vector = NULL; } return InitArrayObject(cx, obj, length, vector); } JSObject * js_InitArrayClass(JSContext *cx, JSObject *obj) { JSObject *proto; proto = JS_InitClass(cx, obj, NULL, &js_ArrayClass, Array, 1, NULL, array_methods, NULL, NULL); /* Initialize the Array prototype object so it gets a length property. */ if (!proto || !InitArrayObject(cx, proto, 0, NULL)) return NULL; return proto; } JSObject * js_NewArrayObject(JSContext *cx, jsuint length, jsval *vector) { JSTempValueRooter tvr; JSObject *obj; obj = js_NewObject(cx, &js_ArrayClass, NULL, NULL); if (!obj) return NULL; JS_PUSH_TEMP_ROOT_OBJECT(cx, obj, &tvr); if (!InitArrayObject(cx, obj, length, vector)) obj = NULL; JS_POP_TEMP_ROOT(cx, &tvr); /* Set/clear newborn root, in case we lost it. */ cx->weakRoots.newborn[GCX_OBJECT] = (JSGCThing *) obj; return obj; } pacparser-1.4.5/src/spidermonkey/js/src/jsarray.h000066400000000000000000000063711464010763600220460ustar00rootroot00000000000000/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- * * ***** BEGIN LICENSE BLOCK ***** * Version: MPL 1.1/GPL 2.0/LGPL 2.1 * * The contents of this file are subject to the Mozilla Public License Version * 1.1 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * http://www.mozilla.org/MPL/ * * Software distributed under the License is distributed on an "AS IS" basis, * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License * for the specific language governing rights and limitations under the * License. * * The Original Code is Mozilla Communicator client code, released * March 31, 1998. * * The Initial Developer of the Original Code is * Netscape Communications Corporation. * Portions created by the Initial Developer are Copyright (C) 1998 * the Initial Developer. All Rights Reserved. * * Contributor(s): * * Alternatively, the contents of this file may be used under the terms of * either of the GNU General Public License Version 2 or later (the "GPL"), * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), * in which case the provisions of the GPL or the LGPL are applicable instead * of those above. If you wish to allow use of your version of this file only * under the terms of either the GPL or the LGPL, and not to allow others to * use your version of this file under the terms of the MPL, indicate your * decision by deleting the provisions above and replace them with the notice * and other provisions required by the GPL or the LGPL. If you do not delete * the provisions above, a recipient may use your version of this file under * the terms of any one of the MPL, the GPL or the LGPL. * * ***** END LICENSE BLOCK ***** */ #ifndef jsarray_h___ #define jsarray_h___ /* * JS Array interface. */ #include "jsprvtd.h" #include "jspubtd.h" JS_BEGIN_EXTERN_C /* Generous sanity-bound on length (in elements) of array initialiser. */ #define ARRAY_INIT_LIMIT JS_BIT(24) extern JSBool js_IdIsIndex(jsval id, jsuint *indexp); extern JSClass js_ArrayClass; extern JSObject * js_InitArrayClass(JSContext *cx, JSObject *obj); extern JSObject * js_NewArrayObject(JSContext *cx, jsuint length, jsval *vector); extern JSBool js_GetLengthProperty(JSContext *cx, JSObject *obj, jsuint *lengthp); extern JSBool js_SetLengthProperty(JSContext *cx, JSObject *obj, jsuint length); extern JSBool js_HasLengthProperty(JSContext *cx, JSObject *obj, jsuint *lengthp); /* * Test whether an object is "array-like". Currently this means whether obj * is an Array or an arguments object. We would like an API, and probably a * way in the language, to bless other objects as array-like: having indexed * properties, and a 'length' property of uint32 value equal to one more than * the greatest index. */ extern JSBool js_IsArrayLike(JSContext *cx, JSObject *obj, JSBool *answerp, jsuint *lengthp); /* * JS-specific heap sort function. */ typedef JSBool (*JSComparator)(void *arg, const void *a, const void *b, int *result); extern JSBool js_HeapSort(void *vec, size_t nel, void *pivot, size_t elsize, JSComparator cmp, void *arg); JS_END_EXTERN_C #endif /* jsarray_h___ */ pacparser-1.4.5/src/spidermonkey/js/src/jsatom.c000066400000000000000000000706261464010763600216670ustar00rootroot00000000000000/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- * * ***** BEGIN LICENSE BLOCK ***** * Version: MPL 1.1/GPL 2.0/LGPL 2.1 * * The contents of this file are subject to the Mozilla Public License Version * 1.1 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * http://www.mozilla.org/MPL/ * * Software distributed under the License is distributed on an "AS IS" basis, * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License * for the specific language governing rights and limitations under the * License. * * The Original Code is Mozilla Communicator client code, released * March 31, 1998. * * The Initial Developer of the Original Code is * Netscape Communications Corporation. * Portions created by the Initial Developer are Copyright (C) 1998 * the Initial Developer. All Rights Reserved. * * Contributor(s): * * Alternatively, the contents of this file may be used under the terms of * either of the GNU General Public License Version 2 or later (the "GPL"), * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), * in which case the provisions of the GPL or the LGPL are applicable instead * of those above. If you wish to allow use of your version of this file only * under the terms of either the GPL or the LGPL, and not to allow others to * use your version of this file under the terms of the MPL, indicate your * decision by deleting the provisions above and replace them with the notice * and other provisions required by the GPL or the LGPL. If you do not delete * the provisions above, a recipient may use your version of this file under * the terms of any one of the MPL, the GPL or the LGPL. * * ***** END LICENSE BLOCK ***** */ /* * JS atom table. */ #include "jsstddef.h" #include #include #include "jstypes.h" #include "jsutil.h" /* Added by JSIFY */ #include "jshash.h" /* Added by JSIFY */ #include "jsprf.h" #include "jsapi.h" #include "jsatom.h" #include "jscntxt.h" #include "jsconfig.h" #include "jsgc.h" #include "jslock.h" #include "jsnum.h" #include "jsscan.h" #include "jsstr.h" JS_FRIEND_API(const char *) js_AtomToPrintableString(JSContext *cx, JSAtom *atom) { return js_ValueToPrintableString(cx, ATOM_KEY(atom)); } /* * Keep this in sync with jspubtd.h -- an assertion below will insist that * its length match the JSType enum's JSTYPE_LIMIT limit value. */ const char *js_type_strs[] = { "undefined", js_object_str, "function", "string", "number", "boolean", "null", "xml", }; JS_STATIC_ASSERT(JSTYPE_LIMIT == sizeof js_type_strs / sizeof js_type_strs[0]); const char *js_boolean_strs[] = { js_false_str, js_true_str }; #define JS_PROTO(name,code,init) const char js_##name##_str[] = #name; #include "jsproto.tbl" #undef JS_PROTO const char *js_proto_strs[JSProto_LIMIT] = { #define JS_PROTO(name,code,init) js_##name##_str, #include "jsproto.tbl" #undef JS_PROTO }; const char js_anonymous_str[] = "anonymous"; const char js_arguments_str[] = "arguments"; const char js_arity_str[] = "arity"; const char js_callee_str[] = "callee"; const char js_caller_str[] = "caller"; const char js_class_prototype_str[] = "prototype"; const char js_constructor_str[] = "constructor"; const char js_count_str[] = "__count__"; const char js_each_str[] = "each"; const char js_eval_str[] = "eval"; const char js_fileName_str[] = "fileName"; const char js_get_str[] = "get"; const char js_getter_str[] = "getter"; const char js_index_str[] = "index"; const char js_input_str[] = "input"; const char js_iterator_str[] = "__iterator__"; const char js_length_str[] = "length"; const char js_lineNumber_str[] = "lineNumber"; const char js_message_str[] = "message"; const char js_name_str[] = "name"; const char js_next_str[] = "next"; const char js_noSuchMethod_str[] = "__noSuchMethod__"; const char js_object_str[] = "object"; const char js_parent_str[] = "__parent__"; const char js_proto_str[] = "__proto__"; const char js_setter_str[] = "setter"; const char js_set_str[] = "set"; const char js_stack_str[] = "stack"; const char js_toSource_str[] = "toSource"; const char js_toString_str[] = "toString"; const char js_toLocaleString_str[] = "toLocaleString"; const char js_valueOf_str[] = "valueOf"; #if JS_HAS_XML_SUPPORT const char js_etago_str[] = ""; const char js_qualifier_str[] = "::"; const char js_space_str[] = " "; const char js_stago_str[] = "<"; const char js_star_str[] = "*"; const char js_starQualifier_str[] = "*::"; const char js_tagc_str[] = ">"; const char js_xml_str[] = "xml"; #endif #if JS_HAS_GENERATORS const char js_close_str[] = "close"; const char js_send_str[] = "send"; #endif #ifdef NARCISSUS const char js_call_str[] = "__call__"; const char js_construct_str[] = "__construct__"; const char js_hasInstance_str[] = "__hasInstance__"; const char js_ExecutionContext_str[] = "ExecutionContext"; const char js_current_str[] = "current"; #endif #define HASH_OBJECT(o) (JS_PTR_TO_UINT32(o) >> JSVAL_TAGBITS) #define HASH_INT(i) ((JSHashNumber)(i)) #define HASH_DOUBLE(dp) ((JSDOUBLE_HI32(*dp) ^ JSDOUBLE_LO32(*dp))) #define HASH_BOOLEAN(b) ((JSHashNumber)(b)) JS_STATIC_DLL_CALLBACK(JSHashNumber) js_hash_atom_key(const void *key) { jsval v; jsdouble *dp; /* Order JSVAL_IS_* tests by likelihood of success. */ v = (jsval)key; if (JSVAL_IS_STRING(v)) return js_HashString(JSVAL_TO_STRING(v)); if (JSVAL_IS_INT(v)) return HASH_INT(JSVAL_TO_INT(v)); if (JSVAL_IS_DOUBLE(v)) { dp = JSVAL_TO_DOUBLE(v); return HASH_DOUBLE(dp); } if (JSVAL_IS_OBJECT(v)) return HASH_OBJECT(JSVAL_TO_OBJECT(v)); if (JSVAL_IS_BOOLEAN(v)) return HASH_BOOLEAN(JSVAL_TO_BOOLEAN(v)); return (JSHashNumber)v; } JS_STATIC_DLL_CALLBACK(intN) js_compare_atom_keys(const void *k1, const void *k2) { jsval v1, v2; v1 = (jsval)k1, v2 = (jsval)k2; if (JSVAL_IS_STRING(v1) && JSVAL_IS_STRING(v2)) return js_EqualStrings(JSVAL_TO_STRING(v1), JSVAL_TO_STRING(v2)); if (JSVAL_IS_DOUBLE(v1) && JSVAL_IS_DOUBLE(v2)) { double d1 = *JSVAL_TO_DOUBLE(v1); double d2 = *JSVAL_TO_DOUBLE(v2); if (JSDOUBLE_IS_NaN(d1)) return JSDOUBLE_IS_NaN(d2); #if defined(XP_WIN) /* XXX MSVC miscompiles such that (NaN == 0) */ if (JSDOUBLE_IS_NaN(d2)) return JS_FALSE; #endif return d1 == d2; } return v1 == v2; } JS_STATIC_DLL_CALLBACK(int) js_compare_stub(const void *v1, const void *v2) { return 1; } /* These next two are exported to jsscript.c and used similarly there. */ void * JS_DLL_CALLBACK js_alloc_table_space(void *priv, size_t size) { return malloc(size); } void JS_DLL_CALLBACK js_free_table_space(void *priv, void *item) { free(item); } JS_STATIC_DLL_CALLBACK(JSHashEntry *) js_alloc_atom(void *priv, const void *key) { JSAtomState *state = (JSAtomState *) priv; JSAtom *atom; atom = (JSAtom *) malloc(sizeof(JSAtom)); if (!atom) return NULL; #ifdef JS_THREADSAFE state->tablegen++; #endif atom->entry.key = key; atom->entry.value = NULL; atom->flags = 0; atom->number = state->number++; return &atom->entry; } JS_STATIC_DLL_CALLBACK(void) js_free_atom(void *priv, JSHashEntry *he, uintN flag) { if (flag != HT_FREE_ENTRY) return; #ifdef JS_THREADSAFE ((JSAtomState *)priv)->tablegen++; #endif free(he); } static JSHashAllocOps atom_alloc_ops = { js_alloc_table_space, js_free_table_space, js_alloc_atom, js_free_atom }; #define JS_ATOM_HASH_SIZE 1024 JSBool js_InitAtomState(JSContext *cx, JSAtomState *state) { state->table = JS_NewHashTable(JS_ATOM_HASH_SIZE, js_hash_atom_key, js_compare_atom_keys, js_compare_stub, &atom_alloc_ops, state); if (!state->table) { JS_ReportOutOfMemory(cx); return JS_FALSE; } state->runtime = cx->runtime; #ifdef JS_THREADSAFE js_InitLock(&state->lock); state->tablegen = 0; #endif if (!js_InitPinnedAtoms(cx, state)) { js_FreeAtomState(cx, state); return JS_FALSE; } return JS_TRUE; } JSBool js_InitPinnedAtoms(JSContext *cx, JSAtomState *state) { uintN i; #define FROB(lval,str) \ JS_BEGIN_MACRO \ if (!(state->lval = js_Atomize(cx, str, strlen(str), ATOM_PINNED))) \ return JS_FALSE; \ JS_END_MACRO for (i = 0; i < JSTYPE_LIMIT; i++) FROB(typeAtoms[i], js_type_strs[i]); for (i = 0; i < JSProto_LIMIT; i++) FROB(classAtoms[i], js_proto_strs[i]); FROB(booleanAtoms[0], js_false_str); FROB(booleanAtoms[1], js_true_str); FROB(nullAtom, js_null_str); FROB(anonymousAtom, js_anonymous_str); FROB(argumentsAtom, js_arguments_str); FROB(arityAtom, js_arity_str); FROB(calleeAtom, js_callee_str); FROB(callerAtom, js_caller_str); FROB(classPrototypeAtom, js_class_prototype_str); FROB(constructorAtom, js_constructor_str); FROB(countAtom, js_count_str); FROB(eachAtom, js_each_str); FROB(evalAtom, js_eval_str); FROB(fileNameAtom, js_fileName_str); FROB(getAtom, js_get_str); FROB(getterAtom, js_getter_str); FROB(indexAtom, js_index_str); FROB(inputAtom, js_input_str); FROB(iteratorAtom, js_iterator_str); FROB(lengthAtom, js_length_str); FROB(lineNumberAtom, js_lineNumber_str); FROB(messageAtom, js_message_str); FROB(nameAtom, js_name_str); FROB(nextAtom, js_next_str); FROB(noSuchMethodAtom, js_noSuchMethod_str); FROB(parentAtom, js_parent_str); FROB(protoAtom, js_proto_str); FROB(setAtom, js_set_str); FROB(setterAtom, js_setter_str); FROB(stackAtom, js_stack_str); FROB(toSourceAtom, js_toSource_str); FROB(toStringAtom, js_toString_str); FROB(toLocaleStringAtom, js_toLocaleString_str); FROB(valueOfAtom, js_valueOf_str); #if JS_HAS_XML_SUPPORT FROB(etagoAtom, js_etago_str); FROB(namespaceAtom, js_namespace_str); FROB(ptagcAtom, js_ptagc_str); FROB(qualifierAtom, js_qualifier_str); FROB(spaceAtom, js_space_str); FROB(stagoAtom, js_stago_str); FROB(starAtom, js_star_str); FROB(starQualifierAtom, js_starQualifier_str); FROB(tagcAtom, js_tagc_str); FROB(xmlAtom, js_xml_str); #endif #if JS_HAS_GENERATORS FROB(closeAtom, js_close_str); #endif #ifdef NARCISSUS FROB(callAtom, js_call_str); FROB(constructAtom, js_construct_str); FROB(hasInstanceAtom, js_hasInstance_str); FROB(ExecutionContextAtom, js_ExecutionContext_str); FROB(currentAtom, js_current_str); #endif #undef FROB memset(&state->lazy, 0, sizeof state->lazy); return JS_TRUE; } /* NB: cx unused; js_FinishAtomState calls us with null cx. */ void js_FreeAtomState(JSContext *cx, JSAtomState *state) { if (state->table) JS_HashTableDestroy(state->table); #ifdef JS_THREADSAFE js_FinishLock(&state->lock); #endif memset(state, 0, sizeof *state); } typedef struct UninternArgs { JSRuntime *rt; jsatomid leaks; } UninternArgs; JS_STATIC_DLL_CALLBACK(intN) js_atom_uninterner(JSHashEntry *he, intN i, void *arg) { JSAtom *atom; UninternArgs *args; atom = (JSAtom *)he; args = (UninternArgs *)arg; if (ATOM_IS_STRING(atom)) js_FinalizeStringRT(args->rt, ATOM_TO_STRING(atom)); else if (ATOM_IS_OBJECT(atom)) args->leaks++; return HT_ENUMERATE_NEXT; } void js_FinishAtomState(JSAtomState *state) { UninternArgs args; if (!state->table) return; args.rt = state->runtime; args.leaks = 0; JS_HashTableEnumerateEntries(state->table, js_atom_uninterner, &args); #ifdef DEBUG if (args.leaks != 0) { fprintf(stderr, "JS engine warning: %lu atoms remain after destroying the JSRuntime.\n" " These atoms may point to freed memory. Things reachable\n" " through them have not been finalized.\n", (unsigned long) args.leaks); } #endif js_FreeAtomState(NULL, state); } typedef struct MarkArgs { JSBool keepAtoms; JSGCThingMarker mark; void *data; } MarkArgs; JS_STATIC_DLL_CALLBACK(intN) js_atom_marker(JSHashEntry *he, intN i, void *arg) { JSAtom *atom; MarkArgs *args; jsval key; atom = (JSAtom *)he; args = (MarkArgs *)arg; if ((atom->flags & (ATOM_PINNED | ATOM_INTERNED)) || args->keepAtoms) { atom->flags |= ATOM_MARK; key = ATOM_KEY(atom); if (JSVAL_IS_GCTHING(key)) args->mark(JSVAL_TO_GCTHING(key), args->data); } return HT_ENUMERATE_NEXT; } void js_MarkAtomState(JSAtomState *state, JSBool keepAtoms, JSGCThingMarker mark, void *data) { MarkArgs args; if (!state->table) return; args.keepAtoms = keepAtoms; args.mark = mark; args.data = data; JS_HashTableEnumerateEntries(state->table, js_atom_marker, &args); } JS_STATIC_DLL_CALLBACK(intN) js_atom_sweeper(JSHashEntry *he, intN i, void *arg) { JSAtom *atom; JSAtomState *state; atom = (JSAtom *)he; if (atom->flags & ATOM_MARK) { atom->flags &= ~ATOM_MARK; state = (JSAtomState *)arg; state->liveAtoms++; return HT_ENUMERATE_NEXT; } JS_ASSERT((atom->flags & (ATOM_PINNED | ATOM_INTERNED)) == 0); atom->entry.key = atom->entry.value = NULL; atom->flags = 0; return HT_ENUMERATE_REMOVE; } void js_SweepAtomState(JSAtomState *state) { state->liveAtoms = 0; if (state->table) JS_HashTableEnumerateEntries(state->table, js_atom_sweeper, state); } JS_STATIC_DLL_CALLBACK(intN) js_atom_unpinner(JSHashEntry *he, intN i, void *arg) { JSAtom *atom; atom = (JSAtom *)he; atom->flags &= ~ATOM_PINNED; return HT_ENUMERATE_NEXT; } void js_UnpinPinnedAtoms(JSAtomState *state) { if (state->table) JS_HashTableEnumerateEntries(state->table, js_atom_unpinner, NULL); } static JSAtom * js_AtomizeHashedKey(JSContext *cx, jsval key, JSHashNumber keyHash, uintN flags) { JSAtomState *state; JSHashTable *table; JSHashEntry *he, **hep; JSAtom *atom; state = &cx->runtime->atomState; JS_LOCK(&state->lock, cx); table = state->table; hep = JS_HashTableRawLookup(table, keyHash, (void *)key); if ((he = *hep) == NULL) { he = JS_HashTableRawAdd(table, hep, keyHash, (void *)key, NULL); if (!he) { JS_ReportOutOfMemory(cx); atom = NULL; goto out; } } atom = (JSAtom *)he; atom->flags |= flags; cx->weakRoots.lastAtom = atom; out: JS_UNLOCK(&state->lock,cx); return atom; } JSAtom * js_AtomizeObject(JSContext *cx, JSObject *obj, uintN flags) { jsval key; JSHashNumber keyHash; /* XXX must be set in the following order or MSVC1.52 will crash */ keyHash = HASH_OBJECT(obj); key = OBJECT_TO_JSVAL(obj); return js_AtomizeHashedKey(cx, key, keyHash, flags); } JSAtom * js_AtomizeBoolean(JSContext *cx, JSBool b, uintN flags) { jsval key; JSHashNumber keyHash; key = BOOLEAN_TO_JSVAL(b); keyHash = HASH_BOOLEAN(b); return js_AtomizeHashedKey(cx, key, keyHash, flags); } JSAtom * js_AtomizeInt(JSContext *cx, jsint i, uintN flags) { jsval key; JSHashNumber keyHash; key = INT_TO_JSVAL(i); keyHash = HASH_INT(i); return js_AtomizeHashedKey(cx, key, keyHash, flags); } /* Worst-case alignment grain and aligning macro for 2x-sized buffer. */ #define ALIGNMENT(t) JS_MAX(JSVAL_ALIGN, sizeof(t)) #define ALIGN(b,t) ((t*) &(b)[ALIGNMENT(t) - (jsuword)(b) % ALIGNMENT(t)]) JSAtom * js_AtomizeDouble(JSContext *cx, jsdouble d, uintN flags) { jsdouble *dp; JSHashNumber keyHash; jsval key; JSAtomState *state; JSHashTable *table; JSHashEntry *he, **hep; JSAtom *atom; char buf[2 * ALIGNMENT(double)]; dp = ALIGN(buf, double); *dp = d; keyHash = HASH_DOUBLE(dp); key = DOUBLE_TO_JSVAL(dp); state = &cx->runtime->atomState; JS_LOCK(&state->lock, cx); table = state->table; hep = JS_HashTableRawLookup(table, keyHash, (void *)key); if ((he = *hep) == NULL) { #ifdef JS_THREADSAFE uint32 gen = state->tablegen; #endif JS_UNLOCK(&state->lock,cx); if (!js_NewDoubleValue(cx, d, &key)) return NULL; JS_LOCK(&state->lock, cx); #ifdef JS_THREADSAFE if (state->tablegen != gen) { hep = JS_HashTableRawLookup(table, keyHash, (void *)key); if ((he = *hep) != NULL) { atom = (JSAtom *)he; goto out; } } #endif he = JS_HashTableRawAdd(table, hep, keyHash, (void *)key, NULL); if (!he) { JS_ReportOutOfMemory(cx); atom = NULL; goto out; } } atom = (JSAtom *)he; atom->flags |= flags; cx->weakRoots.lastAtom = atom; out: JS_UNLOCK(&state->lock,cx); return atom; } /* * To put an atom into the hidden subspace. XOR its keyHash with this value, * which is (sqrt(2)-1) in 32-bit fixed point. */ #define HIDDEN_ATOM_SUBSPACE_KEYHASH 0x6A09E667 JSAtom * js_AtomizeString(JSContext *cx, JSString *str, uintN flags) { JSHashNumber keyHash; jsval key; JSAtomState *state; JSHashTable *table; JSHashEntry *he, **hep; JSAtom *atom; keyHash = js_HashString(str); if (flags & ATOM_HIDDEN) keyHash ^= HIDDEN_ATOM_SUBSPACE_KEYHASH; key = STRING_TO_JSVAL(str); state = &cx->runtime->atomState; JS_LOCK(&state->lock, cx); table = state->table; hep = JS_HashTableRawLookup(table, keyHash, (void *)key); if ((he = *hep) == NULL) { #ifdef JS_THREADSAFE uint32 gen = state->tablegen; JS_UNLOCK(&state->lock, cx); #endif if (flags & ATOM_TMPSTR) { str = (flags & ATOM_NOCOPY) ? js_NewString(cx, str->chars, str->length, 0) : js_NewStringCopyN(cx, str->chars, str->length, 0); if (!str) return NULL; key = STRING_TO_JSVAL(str); } else { if (!JS_MakeStringImmutable(cx, str)) return NULL; } #ifdef JS_THREADSAFE JS_LOCK(&state->lock, cx); if (state->tablegen != gen) { hep = JS_HashTableRawLookup(table, keyHash, (void *)key); if ((he = *hep) != NULL) { atom = (JSAtom *)he; if (flags & ATOM_NOCOPY) str->chars = NULL; goto out; } } #endif he = JS_HashTableRawAdd(table, hep, keyHash, (void *)key, NULL); if (!he) { JS_ReportOutOfMemory(cx); atom = NULL; goto out; } } atom = (JSAtom *)he; atom->flags |= flags & (ATOM_PINNED | ATOM_INTERNED | ATOM_HIDDEN); cx->weakRoots.lastAtom = atom; out: JS_UNLOCK(&state->lock,cx); return atom; } JS_FRIEND_API(JSAtom *) js_Atomize(JSContext *cx, const char *bytes, size_t length, uintN flags) { jschar *chars; JSString *str; JSAtom *atom; char buf[2 * ALIGNMENT(JSString)]; /* * Avoiding the malloc in js_InflateString on shorter strings saves us * over 20,000 malloc calls on mozilla browser startup. This compares to * only 131 calls where the string is longer than a 31 char (net) buffer. * The vast majority of atomized strings are already in the hashtable. So * js_AtomizeString rarely has to copy the temp string we make. */ #define ATOMIZE_BUF_MAX 32 jschar inflated[ATOMIZE_BUF_MAX]; size_t inflatedLength = ATOMIZE_BUF_MAX - 1; if (length < ATOMIZE_BUF_MAX) { js_InflateStringToBuffer(cx, bytes, length, inflated, &inflatedLength); inflated[inflatedLength] = 0; chars = inflated; } else { inflatedLength = length; chars = js_InflateString(cx, bytes, &inflatedLength); if (!chars) return NULL; flags |= ATOM_NOCOPY; } str = ALIGN(buf, JSString); str->chars = chars; str->length = inflatedLength; atom = js_AtomizeString(cx, str, ATOM_TMPSTR | flags); if (chars != inflated && (!atom || ATOM_TO_STRING(atom)->chars != chars)) JS_free(cx, chars); return atom; } JS_FRIEND_API(JSAtom *) js_AtomizeChars(JSContext *cx, const jschar *chars, size_t length, uintN flags) { JSString *str; char buf[2 * ALIGNMENT(JSString)]; str = ALIGN(buf, JSString); str->chars = (jschar *)chars; str->length = length; return js_AtomizeString(cx, str, ATOM_TMPSTR | flags); } JSAtom * js_GetExistingStringAtom(JSContext *cx, const jschar *chars, size_t length) { JSString *str; char buf[2 * ALIGNMENT(JSString)]; JSHashNumber keyHash; jsval key; JSAtomState *state; JSHashTable *table; JSHashEntry **hep; str = ALIGN(buf, JSString); str->chars = (jschar *)chars; str->length = length; keyHash = js_HashString(str); key = STRING_TO_JSVAL(str); state = &cx->runtime->atomState; JS_LOCK(&state->lock, cx); table = state->table; hep = JS_HashTableRawLookup(table, keyHash, (void *)key); JS_UNLOCK(&state->lock, cx); return (hep) ? (JSAtom *)*hep : NULL; } JSAtom * js_AtomizeValue(JSContext *cx, jsval value, uintN flags) { if (JSVAL_IS_STRING(value)) return js_AtomizeString(cx, JSVAL_TO_STRING(value), flags); if (JSVAL_IS_INT(value)) return js_AtomizeInt(cx, JSVAL_TO_INT(value), flags); if (JSVAL_IS_DOUBLE(value)) return js_AtomizeDouble(cx, *JSVAL_TO_DOUBLE(value), flags); if (JSVAL_IS_OBJECT(value)) return js_AtomizeObject(cx, JSVAL_TO_OBJECT(value), flags); if (JSVAL_IS_BOOLEAN(value)) return js_AtomizeBoolean(cx, JSVAL_TO_BOOLEAN(value), flags); return js_AtomizeHashedKey(cx, value, (JSHashNumber)value, flags); } JSAtom * js_ValueToStringAtom(JSContext *cx, jsval v) { JSString *str; str = js_ValueToString(cx, v); if (!str) return NULL; return js_AtomizeString(cx, str, 0); } JS_STATIC_DLL_CALLBACK(JSHashNumber) js_hash_atom_ptr(const void *key) { const JSAtom *atom = key; return atom->number; } JS_STATIC_DLL_CALLBACK(void *) js_alloc_temp_space(void *priv, size_t size) { JSContext *cx = priv; void *space; JS_ARENA_ALLOCATE(space, &cx->tempPool, size); if (!space) JS_ReportOutOfMemory(cx); return space; } JS_STATIC_DLL_CALLBACK(void) js_free_temp_space(void *priv, void *item) { } JS_STATIC_DLL_CALLBACK(JSHashEntry *) js_alloc_temp_entry(void *priv, const void *key) { JSContext *cx = priv; JSAtomListElement *ale; JS_ARENA_ALLOCATE_TYPE(ale, JSAtomListElement, &cx->tempPool); if (!ale) { JS_ReportOutOfMemory(cx); return NULL; } return &ale->entry; } JS_STATIC_DLL_CALLBACK(void) js_free_temp_entry(void *priv, JSHashEntry *he, uintN flag) { } static JSHashAllocOps temp_alloc_ops = { js_alloc_temp_space, js_free_temp_space, js_alloc_temp_entry, js_free_temp_entry }; JSAtomListElement * js_IndexAtom(JSContext *cx, JSAtom *atom, JSAtomList *al) { JSAtomListElement *ale, *ale2, *next; JSHashEntry **hep; ATOM_LIST_LOOKUP(ale, hep, al, atom); if (!ale) { if (al->count < 10) { /* Few enough for linear search, no hash table needed. */ JS_ASSERT(!al->table); ale = (JSAtomListElement *)js_alloc_temp_entry(cx, atom); if (!ale) return NULL; ALE_SET_ATOM(ale, atom); ALE_SET_NEXT(ale, al->list); al->list = ale; } else { /* We want to hash. Have we already made a hash table? */ if (!al->table) { /* No hash table yet, so hep had better be null! */ JS_ASSERT(!hep); al->table = JS_NewHashTable(al->count + 1, js_hash_atom_ptr, JS_CompareValues, JS_CompareValues, &temp_alloc_ops, cx); if (!al->table) return NULL; /* * Set ht->nentries explicitly, because we are moving entries * from al to ht, not calling JS_HashTable(Raw|)Add. */ al->table->nentries = al->count; /* Insert each ale on al->list into the new hash table. */ for (ale2 = al->list; ale2; ale2 = next) { next = ALE_NEXT(ale2); ale2->entry.keyHash = ALE_ATOM(ale2)->number; hep = JS_HashTableRawLookup(al->table, ale2->entry.keyHash, ale2->entry.key); ALE_SET_NEXT(ale2, *hep); *hep = &ale2->entry; } al->list = NULL; /* Set hep for insertion of atom's ale, immediately below. */ hep = JS_HashTableRawLookup(al->table, atom->number, atom); } /* Finally, add an entry for atom into the hash bucket at hep. */ ale = (JSAtomListElement *) JS_HashTableRawAdd(al->table, hep, atom->number, atom, NULL); if (!ale) return NULL; } ALE_SET_INDEX(ale, al->count++); } return ale; } JS_FRIEND_API(JSAtom *) js_GetAtom(JSContext *cx, JSAtomMap *map, jsatomid i) { JSAtom *atom; static JSAtom dummy; JS_ASSERT(map->vector && i < map->length); if (!map->vector || i >= map->length) { char numBuf[12]; JS_snprintf(numBuf, sizeof numBuf, "%lu", (unsigned long)i); JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_BAD_ATOMIC_NUMBER, numBuf); return &dummy; } atom = map->vector[i]; JS_ASSERT(atom); return atom; } JS_STATIC_DLL_CALLBACK(intN) js_map_atom(JSHashEntry *he, intN i, void *arg) { JSAtomListElement *ale = (JSAtomListElement *)he; JSAtom **vector = arg; vector[ALE_INDEX(ale)] = ALE_ATOM(ale); return HT_ENUMERATE_NEXT; } #ifdef DEBUG static jsrefcount js_atom_map_count; static jsrefcount js_atom_map_hash_table_count; #endif JS_FRIEND_API(JSBool) js_InitAtomMap(JSContext *cx, JSAtomMap *map, JSAtomList *al) { JSAtom **vector; JSAtomListElement *ale; uint32 count; #ifdef DEBUG JS_ATOMIC_INCREMENT(&js_atom_map_count); #endif ale = al->list; if (!ale && !al->table) { map->vector = NULL; map->length = 0; return JS_TRUE; } count = al->count; if (count >= ATOM_INDEX_LIMIT) { JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_TOO_MANY_LITERALS); return JS_FALSE; } vector = (JSAtom **) JS_malloc(cx, (size_t) count * sizeof *vector); if (!vector) return JS_FALSE; if (al->table) { #ifdef DEBUG JS_ATOMIC_INCREMENT(&js_atom_map_hash_table_count); #endif JS_HashTableEnumerateEntries(al->table, js_map_atom, vector); } else { do { vector[ALE_INDEX(ale)] = ALE_ATOM(ale); } while ((ale = ALE_NEXT(ale)) != NULL); } ATOM_LIST_INIT(al); map->vector = vector; map->length = (jsatomid)count; return JS_TRUE; } JS_FRIEND_API(void) js_FreeAtomMap(JSContext *cx, JSAtomMap *map) { if (map->vector) { JS_free(cx, map->vector); map->vector = NULL; } map->length = 0; } pacparser-1.4.5/src/spidermonkey/js/src/jsatom.h000066400000000000000000000420221464010763600216610ustar00rootroot00000000000000/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- * * ***** BEGIN LICENSE BLOCK ***** * Version: MPL 1.1/GPL 2.0/LGPL 2.1 * * The contents of this file are subject to the Mozilla Public License Version * 1.1 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * http://www.mozilla.org/MPL/ * * Software distributed under the License is distributed on an "AS IS" basis, * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License * for the specific language governing rights and limitations under the * License. * * The Original Code is Mozilla Communicator client code, released * March 31, 1998. * * The Initial Developer of the Original Code is * Netscape Communications Corporation. * Portions created by the Initial Developer are Copyright (C) 1998 * the Initial Developer. All Rights Reserved. * * Contributor(s): * * Alternatively, the contents of this file may be used under the terms of * either of the GNU General Public License Version 2 or later (the "GPL"), * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), * in which case the provisions of the GPL or the LGPL are applicable instead * of those above. If you wish to allow use of your version of this file only * under the terms of either the GPL or the LGPL, and not to allow others to * use your version of this file under the terms of the MPL, indicate your * decision by deleting the provisions above and replace them with the notice * and other provisions required by the GPL or the LGPL. If you do not delete * the provisions above, a recipient may use your version of this file under * the terms of any one of the MPL, the GPL or the LGPL. * * ***** END LICENSE BLOCK ***** */ #ifndef jsatom_h___ #define jsatom_h___ /* * JS atom table. */ #include #include "jstypes.h" #include "jshash.h" /* Added by JSIFY */ #include "jsapi.h" #include "jsprvtd.h" #include "jspubtd.h" #ifdef JS_THREADSAFE #include "jslock.h" #endif JS_BEGIN_EXTERN_C #define ATOM_PINNED 0x01 /* atom is pinned against GC */ #define ATOM_INTERNED 0x02 /* pinned variant for JS_Intern* API */ #define ATOM_MARK 0x04 /* atom is reachable via GC */ #define ATOM_HIDDEN 0x08 /* atom is in special hidden subspace */ #define ATOM_NOCOPY 0x40 /* don't copy atom string bytes */ #define ATOM_TMPSTR 0x80 /* internal, to avoid extra string */ struct JSAtom { JSHashEntry entry; /* key is jsval or unhidden atom if ATOM_HIDDEN */ uint32 flags; /* pinned, interned, and mark flags */ jsatomid number; /* atom serial number and hash code */ }; #define ATOM_KEY(atom) ((jsval)(atom)->entry.key) #define ATOM_IS_OBJECT(atom) JSVAL_IS_OBJECT(ATOM_KEY(atom)) #define ATOM_TO_OBJECT(atom) JSVAL_TO_OBJECT(ATOM_KEY(atom)) #define ATOM_IS_INT(atom) JSVAL_IS_INT(ATOM_KEY(atom)) #define ATOM_TO_INT(atom) JSVAL_TO_INT(ATOM_KEY(atom)) #define ATOM_IS_DOUBLE(atom) JSVAL_IS_DOUBLE(ATOM_KEY(atom)) #define ATOM_TO_DOUBLE(atom) JSVAL_TO_DOUBLE(ATOM_KEY(atom)) #define ATOM_IS_STRING(atom) JSVAL_IS_STRING(ATOM_KEY(atom)) #define ATOM_TO_STRING(atom) JSVAL_TO_STRING(ATOM_KEY(atom)) #define ATOM_IS_BOOLEAN(atom) JSVAL_IS_BOOLEAN(ATOM_KEY(atom)) #define ATOM_TO_BOOLEAN(atom) JSVAL_TO_BOOLEAN(ATOM_KEY(atom)) /* * Return a printable, lossless char[] representation of a string-type atom. * The lifetime of the result extends at least until the next GC activation, * longer if cx's string newborn root is not overwritten. */ extern JS_FRIEND_API(const char *) js_AtomToPrintableString(JSContext *cx, JSAtom *atom); struct JSAtomListElement { JSHashEntry entry; }; #define ALE_ATOM(ale) ((JSAtom *) (ale)->entry.key) #define ALE_INDEX(ale) ((jsatomid) JS_PTR_TO_UINT32((ale)->entry.value)) #define ALE_JSOP(ale) ((JSOp) (ale)->entry.value) #define ALE_VALUE(ale) ((jsval) (ale)->entry.value) #define ALE_NEXT(ale) ((JSAtomListElement *) (ale)->entry.next) #define ALE_SET_ATOM(ale,atom) ((ale)->entry.key = (const void *)(atom)) #define ALE_SET_INDEX(ale,index)((ale)->entry.value = JS_UINT32_TO_PTR(index)) #define ALE_SET_JSOP(ale,op) ((ale)->entry.value = JS_UINT32_TO_PTR(op)) #define ALE_SET_VALUE(ale,val) ((ale)->entry.value = (JSHashEntry *)(val)) #define ALE_SET_NEXT(ale,link) ((ale)->entry.next = (JSHashEntry *)(link)) struct JSAtomList { JSAtomListElement *list; /* literals indexed for mapping */ JSHashTable *table; /* hash table if list gets too long */ jsuint count; /* count of indexed literals */ }; #define ATOM_LIST_INIT(al) ((al)->list = NULL, (al)->table = NULL, \ (al)->count = 0) #define ATOM_LIST_SEARCH(_ale,_al,_atom) \ JS_BEGIN_MACRO \ JSHashEntry **_hep; \ ATOM_LIST_LOOKUP(_ale, _hep, _al, _atom); \ JS_END_MACRO #define ATOM_LIST_LOOKUP(_ale,_hep,_al,_atom) \ JS_BEGIN_MACRO \ if ((_al)->table) { \ _hep = JS_HashTableRawLookup((_al)->table, _atom->number, _atom); \ _ale = *_hep ? (JSAtomListElement *) *_hep : NULL; \ } else { \ JSAtomListElement **_alep = &(_al)->list; \ _hep = NULL; \ while ((_ale = *_alep) != NULL) { \ if (ALE_ATOM(_ale) == (_atom)) { \ /* Hit, move atom's element to the front of the list. */ \ *_alep = ALE_NEXT(_ale); \ ALE_SET_NEXT(_ale, (_al)->list); \ (_al)->list = _ale; \ break; \ } \ _alep = (JSAtomListElement **)&_ale->entry.next; \ } \ } \ JS_END_MACRO struct JSAtomMap { JSAtom **vector; /* array of ptrs to indexed atoms */ jsatomid length; /* count of (to-be-)indexed atoms */ }; struct JSAtomState { JSRuntime *runtime; /* runtime that owns us */ JSHashTable *table; /* hash table containing all atoms */ jsatomid number; /* one beyond greatest atom number */ jsatomid liveAtoms; /* number of live atoms after last GC */ /* The rt->emptyString atom, see jsstr.c's js_InitRuntimeStringState. */ JSAtom *emptyAtom; /* Type names and value literals. */ JSAtom *typeAtoms[JSTYPE_LIMIT]; JSAtom *booleanAtoms[2]; JSAtom *nullAtom; /* Standard class constructor or prototype names. */ JSAtom *classAtoms[JSProto_LIMIT]; /* Various built-in or commonly-used atoms, pinned on first context. */ JSAtom *anonymousAtom; JSAtom *argumentsAtom; JSAtom *arityAtom; JSAtom *calleeAtom; JSAtom *callerAtom; JSAtom *classPrototypeAtom; JSAtom *closeAtom; JSAtom *constructorAtom; JSAtom *countAtom; JSAtom *eachAtom; JSAtom *etagoAtom; JSAtom *evalAtom; JSAtom *fileNameAtom; JSAtom *getAtom; JSAtom *getterAtom; JSAtom *indexAtom; JSAtom *inputAtom; JSAtom *iteratorAtom; JSAtom *lengthAtom; JSAtom *lineNumberAtom; JSAtom *messageAtom; JSAtom *nameAtom; JSAtom *namespaceAtom; JSAtom *nextAtom; JSAtom *noSuchMethodAtom; JSAtom *parentAtom; JSAtom *protoAtom; JSAtom *ptagcAtom; JSAtom *qualifierAtom; JSAtom *setAtom; JSAtom *setterAtom; JSAtom *spaceAtom; JSAtom *stackAtom; JSAtom *stagoAtom; JSAtom *starAtom; JSAtom *starQualifierAtom; JSAtom *tagcAtom; JSAtom *toLocaleStringAtom; JSAtom *toSourceAtom; JSAtom *toStringAtom; JSAtom *valueOfAtom; JSAtom *xmlAtom; /* Less frequently used atoms, pinned lazily by JS_ResolveStandardClass. */ struct { JSAtom *InfinityAtom; JSAtom *NaNAtom; JSAtom *XMLListAtom; JSAtom *decodeURIAtom; JSAtom *decodeURIComponentAtom; JSAtom *defineGetterAtom; JSAtom *defineSetterAtom; JSAtom *encodeURIAtom; JSAtom *encodeURIComponentAtom; JSAtom *escapeAtom; JSAtom *functionNamespaceURIAtom; JSAtom *hasOwnPropertyAtom; JSAtom *isFiniteAtom; JSAtom *isNaNAtom; JSAtom *isPrototypeOfAtom; JSAtom *isXMLNameAtom; JSAtom *lookupGetterAtom; JSAtom *lookupSetterAtom; JSAtom *parseFloatAtom; JSAtom *parseIntAtom; JSAtom *propertyIsEnumerableAtom; JSAtom *unescapeAtom; JSAtom *unevalAtom; JSAtom *unwatchAtom; JSAtom *watchAtom; } lazy; #ifdef JS_THREADSAFE JSThinLock lock; volatile uint32 tablegen; #endif #ifdef NARCISSUS JSAtom *callAtom; JSAtom *constructAtom; JSAtom *hasInstanceAtom; JSAtom *ExecutionContextAtom; JSAtom *currentAtom; #endif }; #define CLASS_ATOM(cx,name) \ ((cx)->runtime->atomState.classAtoms[JSProto_##name]) /* Well-known predefined strings and their atoms. */ extern const char *js_type_strs[]; extern const char *js_boolean_strs[]; extern const char *js_proto_strs[]; #define JS_PROTO(name,code,init) extern const char js_##name##_str[]; #include "jsproto.tbl" #undef JS_PROTO extern const char js_anonymous_str[]; extern const char js_arguments_str[]; extern const char js_arity_str[]; extern const char js_callee_str[]; extern const char js_caller_str[]; extern const char js_class_prototype_str[]; extern const char js_close_str[]; extern const char js_constructor_str[]; extern const char js_count_str[]; extern const char js_etago_str[]; extern const char js_each_str[]; extern const char js_eval_str[]; extern const char js_fileName_str[]; extern const char js_get_str[]; extern const char js_getter_str[]; extern const char js_index_str[]; extern const char js_input_str[]; extern const char js_iterator_str[]; extern const char js_length_str[]; extern const char js_lineNumber_str[]; extern const char js_message_str[]; extern const char js_name_str[]; extern const char js_namespace_str[]; extern const char js_next_str[]; extern const char js_noSuchMethod_str[]; extern const char js_object_str[]; extern const char js_parent_str[]; extern const char js_private_str[]; extern const char js_proto_str[]; extern const char js_ptagc_str[]; extern const char js_qualifier_str[]; extern const char js_send_str[]; extern const char js_setter_str[]; extern const char js_set_str[]; extern const char js_space_str[]; extern const char js_stack_str[]; extern const char js_stago_str[]; extern const char js_star_str[]; extern const char js_starQualifier_str[]; extern const char js_tagc_str[]; extern const char js_toSource_str[]; extern const char js_toString_str[]; extern const char js_toLocaleString_str[]; extern const char js_valueOf_str[]; extern const char js_xml_str[]; #ifdef NARCISSUS extern const char js_call_str[]; extern const char js_construct_str[]; extern const char js_hasInstance_str[]; extern const char js_ExecutionContext_str[]; extern const char js_current_str[]; #endif /* * Initialize atom state. Return true on success, false with an out of * memory error report on failure. */ extern JSBool js_InitAtomState(JSContext *cx, JSAtomState *state); /* * Free and clear atom state (except for any interned string atoms). */ extern void js_FreeAtomState(JSContext *cx, JSAtomState *state); /* * Interned strings are atoms that live until state's runtime is destroyed. * This function frees all interned string atoms, and then frees and clears * state's members (just as js_FreeAtomState does), unless there aren't any * interned strings in state -- in which case state must be "free" already. * * NB: js_FreeAtomState is called for each "last" context being destroyed in * a runtime, where there may yet be another context created in the runtime; * whereas js_FinishAtomState is called from JS_DestroyRuntime, when we know * that no more contexts will be created. Thus we minimize garbage during * context-free episodes on a runtime, while preserving atoms created by the * JS_Intern*String APIs for the life of the runtime. */ extern void js_FinishAtomState(JSAtomState *state); /* * Atom garbage collection hooks. */ typedef void (*JSGCThingMarker)(void *thing, void *data); extern void js_MarkAtomState(JSAtomState *state, JSBool keepAtoms, JSGCThingMarker mark, void *data); extern void js_SweepAtomState(JSAtomState *state); extern JSBool js_InitPinnedAtoms(JSContext *cx, JSAtomState *state); extern void js_UnpinPinnedAtoms(JSAtomState *state); /* * Find or create the atom for an object. If we create a new atom, give it the * type indicated in flags. Return 0 on failure to allocate memory. */ extern JSAtom * js_AtomizeObject(JSContext *cx, JSObject *obj, uintN flags); /* * Find or create the atom for a Boolean value. If we create a new atom, give * it the type indicated in flags. Return 0 on failure to allocate memory. */ extern JSAtom * js_AtomizeBoolean(JSContext *cx, JSBool b, uintN flags); /* * Find or create the atom for an integer value. If we create a new atom, give * it the type indicated in flags. Return 0 on failure to allocate memory. */ extern JSAtom * js_AtomizeInt(JSContext *cx, jsint i, uintN flags); /* * Find or create the atom for a double value. If we create a new atom, give * it the type indicated in flags. Return 0 on failure to allocate memory. */ extern JSAtom * js_AtomizeDouble(JSContext *cx, jsdouble d, uintN flags); /* * Find or create the atom for a string. If we create a new atom, give it the * type indicated in flags. Return 0 on failure to allocate memory. */ extern JSAtom * js_AtomizeString(JSContext *cx, JSString *str, uintN flags); extern JS_FRIEND_API(JSAtom *) js_Atomize(JSContext *cx, const char *bytes, size_t length, uintN flags); extern JS_FRIEND_API(JSAtom *) js_AtomizeChars(JSContext *cx, const jschar *chars, size_t length, uintN flags); /* * Return an existing atom for the given char array or null if the char * sequence is currently not atomized. */ extern JSAtom * js_GetExistingStringAtom(JSContext *cx, const jschar *chars, size_t length); /* * This variant handles all value tag types. */ extern JSAtom * js_AtomizeValue(JSContext *cx, jsval value, uintN flags); /* * Convert v to an atomized string. */ extern JSAtom * js_ValueToStringAtom(JSContext *cx, jsval v); /* * Assign atom an index and insert it on al. */ extern JSAtomListElement * js_IndexAtom(JSContext *cx, JSAtom *atom, JSAtomList *al); /* * Get the atom with index i from map. */ extern JS_FRIEND_API(JSAtom *) js_GetAtom(JSContext *cx, JSAtomMap *map, jsatomid i); /* * For all unmapped atoms recorded in al, add a mapping from the atom's index * to its address. The GC must not run until all indexed atoms in atomLists * have been mapped by scripts connected to live objects (Function and Script * class objects have scripts as/in their private data -- the GC knows about * these two classes). */ extern JS_FRIEND_API(JSBool) js_InitAtomMap(JSContext *cx, JSAtomMap *map, JSAtomList *al); /* * Free map->vector and clear map. */ extern JS_FRIEND_API(void) js_FreeAtomMap(JSContext *cx, JSAtomMap *map); JS_END_EXTERN_C #endif /* jsatom_h___ */ pacparser-1.4.5/src/spidermonkey/js/src/jsbit.h000066400000000000000000000202571464010763600215050ustar00rootroot00000000000000/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* ***** BEGIN LICENSE BLOCK ***** * Version: MPL 1.1/GPL 2.0/LGPL 2.1 * * The contents of this file are subject to the Mozilla Public License Version * 1.1 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * http://www.mozilla.org/MPL/ * * Software distributed under the License is distributed on an "AS IS" basis, * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License * for the specific language governing rights and limitations under the * License. * * The Original Code is Mozilla Communicator client code, released * March 31, 1998. * * The Initial Developer of the Original Code is * Netscape Communications Corporation. * Portions created by the Initial Developer are Copyright (C) 1998 * the Initial Developer. All Rights Reserved. * * Contributor(s): * * Alternatively, the contents of this file may be used under the terms of * either of the GNU General Public License Version 2 or later (the "GPL"), * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), * in which case the provisions of the GPL or the LGPL are applicable instead * of those above. If you wish to allow use of your version of this file only * under the terms of either the GPL or the LGPL, and not to allow others to * use your version of this file under the terms of the MPL, indicate your * decision by deleting the provisions above and replace them with the notice * and other provisions required by the GPL or the LGPL. If you do not delete * the provisions above, a recipient may use your version of this file under * the terms of any one of the MPL, the GPL or the LGPL. * * ***** END LICENSE BLOCK ***** */ #ifndef jsbit_h___ #define jsbit_h___ #include "jstypes.h" #include "jsutil.h" JS_BEGIN_EXTERN_C /* ** A jsbitmap_t is a long integer that can be used for bitmaps */ typedef JSUword jsbitmap_t; /* NSPR name, a la Unix system types */ typedef jsbitmap_t jsbitmap; /* JS-style scalar typedef name */ #define JS_TEST_BIT(_map,_bit) \ ((_map)[(_bit)>>JS_BITS_PER_WORD_LOG2] & (1L << ((_bit) & (JS_BITS_PER_WORD-1)))) #define JS_SET_BIT(_map,_bit) \ ((_map)[(_bit)>>JS_BITS_PER_WORD_LOG2] |= (1L << ((_bit) & (JS_BITS_PER_WORD-1)))) #define JS_CLEAR_BIT(_map,_bit) \ ((_map)[(_bit)>>JS_BITS_PER_WORD_LOG2] &= ~(1L << ((_bit) & (JS_BITS_PER_WORD-1)))) /* ** Compute the log of the least power of 2 greater than or equal to n */ extern JS_PUBLIC_API(JSIntn) JS_CeilingLog2(JSUint32 i); /* ** Compute the log of the greatest power of 2 less than or equal to n */ extern JS_PUBLIC_API(JSIntn) JS_FloorLog2(JSUint32 i); /* * Check if __builtin_clz is available which apeared first in GCC 3.4. * The built-in allows to speedup calculations of ceiling/floor log2, * see bug 327129. */ #if __GNUC__ >= 4 || (__GNUC__ == 3 && __GNUC_MINOR__ >= 4) # define JS_HAS_GCC_BUILTIN_CLZ #endif /* ** Macro version of JS_CeilingLog2: Compute the log of the least power of ** 2 greater than or equal to _n. The result is returned in _log2. */ #ifdef JS_HAS_GCC_BUILTIN_CLZ /* * Use __builtin_clz or count-leading-zeros to calculate ceil(log2(_n)). * The macro checks for "n <= 1" and not "n != 0" as __builtin_clz(0) is * undefined. */ # define JS_CEILING_LOG2(_log2,_n) \ JS_BEGIN_MACRO \ JS_STATIC_ASSERT(sizeof(unsigned int) == sizeof(JSUint32)); \ unsigned int j_ = (unsigned int)(_n); \ (_log2) = (j_ <= 1 ? 0 : 32 - __builtin_clz(j_ - 1)); \ JS_END_MACRO #else # define JS_CEILING_LOG2(_log2,_n) \ JS_BEGIN_MACRO \ JSUint32 j_ = (JSUint32)(_n); \ (_log2) = 0; \ if ((j_) & ((j_)-1)) \ (_log2) += 1; \ if ((j_) >> 16) \ (_log2) += 16, (j_) >>= 16; \ if ((j_) >> 8) \ (_log2) += 8, (j_) >>= 8; \ if ((j_) >> 4) \ (_log2) += 4, (j_) >>= 4; \ if ((j_) >> 2) \ (_log2) += 2, (j_) >>= 2; \ if ((j_) >> 1) \ (_log2) += 1; \ JS_END_MACRO #endif /* ** Macro version of JS_FloorLog2: Compute the log of the greatest power of ** 2 less than or equal to _n. The result is returned in _log2. ** ** This is equivalent to finding the highest set bit in the word. */ #if JS_GCC_HAS_BUILTIN_CLZ /* * Use __builtin_clz or count-leading-zeros to calculate floor(log2(_n)). * Since __builtin_clz(0) is undefined, the macro set the loweset bit to 1 * to ensure 0 result when _n == 0. */ # define JS_FLOOR_LOG2(_log2,_n) \ JS_BEGIN_MACRO \ JS_STATIC_ASSERT(sizeof(unsigned int) == sizeof(JSUint32)); \ (_log2) = 31 - __builtin_clz(((unsigned int)(_n)) | 1); \ JS_END_MACRO #else # define JS_FLOOR_LOG2(_log2,_n) \ JS_BEGIN_MACRO \ JSUint32 j_ = (JSUint32)(_n); \ (_log2) = 0; \ if ((j_) >> 16) \ (_log2) += 16, (j_) >>= 16; \ if ((j_) >> 8) \ (_log2) += 8, (j_) >>= 8; \ if ((j_) >> 4) \ (_log2) += 4, (j_) >>= 4; \ if ((j_) >> 2) \ (_log2) += 2, (j_) >>= 2; \ if ((j_) >> 1) \ (_log2) += 1; \ JS_END_MACRO #endif /* * Internal function. * Compute the log of the least power of 2 greater than or equal to n. * This is a version of JS_CeilingLog2 that operates on jsuword with * CPU-dependant size. */ #define JS_CEILING_LOG2W(n) ((n) <= 1 ? 0 : 1 + JS_FLOOR_LOG2W((n) - 1)) /* * Internal function. * Compute the log of the greatest power of 2 less than or equal to n. * This is a version of JS_FloorLog2 that operates on jsuword with * CPU-dependant size and requires that n != 0. */ #define JS_FLOOR_LOG2W(n) (JS_ASSERT((n) != 0), js_FloorLog2wImpl(n)) #ifdef JS_HAS_GCC_BUILTIN_CLZ # if JS_BYTES_PER_WORD == 4 JS_STATIC_ASSERT(sizeof(unsigned) == sizeof(JSUword)); # define js_FloorLog2wImpl(n) \ ((JSUword)(JS_BITS_PER_WORD - 1 - __builtin_clz(n))) # elif JS_BYTES_PER_WORD == 8 JS_STATIC_ASSERT(sizeof(unsigned long long) == sizeof(JSUword)); # define js_FloorLog2wImpl(n) \ ((JSUword)(JS_BITS_PER_WORD - 1 - __builtin_clzll(n))) # else # error "NOT SUPPORTED" # endif #else # if JS_BYTES_PER_WORD == 4 # define js_FloorLog2wImpl(n) ((JSUword)JS_FloorLog2(n)) # elif JS_BYTES_PER_WORD == 8 extern JSUword js_FloorLog2wImpl(JSUword n); # else # error "NOT SUPPORTED" # endif #endif JS_END_EXTERN_C #endif /* jsbit_h___ */ pacparser-1.4.5/src/spidermonkey/js/src/jsbool.c000066400000000000000000000152421464010763600216530ustar00rootroot00000000000000/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- * * ***** BEGIN LICENSE BLOCK ***** * Version: MPL 1.1/GPL 2.0/LGPL 2.1 * * The contents of this file are subject to the Mozilla Public License Version * 1.1 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * http://www.mozilla.org/MPL/ * * Software distributed under the License is distributed on an "AS IS" basis, * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License * for the specific language governing rights and limitations under the * License. * * The Original Code is Mozilla Communicator client code, released * March 31, 1998. * * The Initial Developer of the Original Code is * Netscape Communications Corporation. * Portions created by the Initial Developer are Copyright (C) 1998 * the Initial Developer. All Rights Reserved. * * Contributor(s): * * Alternatively, the contents of this file may be used under the terms of * either of the GNU General Public License Version 2 or later (the "GPL"), * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), * in which case the provisions of the GPL or the LGPL are applicable instead * of those above. If you wish to allow use of your version of this file only * under the terms of either the GPL or the LGPL, and not to allow others to * use your version of this file under the terms of the MPL, indicate your * decision by deleting the provisions above and replace them with the notice * and other provisions required by the GPL or the LGPL. If you do not delete * the provisions above, a recipient may use your version of this file under * the terms of any one of the MPL, the GPL or the LGPL. * * ***** END LICENSE BLOCK ***** */ /* * JS boolean implementation. */ #include "jsstddef.h" #include "jstypes.h" #include "jsutil.h" /* Added by JSIFY */ #include "jsapi.h" #include "jsatom.h" #include "jsbool.h" #include "jscntxt.h" #include "jsconfig.h" #include "jsinterp.h" #include "jslock.h" #include "jsnum.h" #include "jsobj.h" #include "jsstr.h" JSClass js_BooleanClass = { "Boolean", JSCLASS_HAS_PRIVATE | JSCLASS_HAS_CACHED_PROTO(JSProto_Boolean), JS_PropertyStub, JS_PropertyStub, JS_PropertyStub, JS_PropertyStub, JS_EnumerateStub, JS_ResolveStub, JS_ConvertStub, JS_FinalizeStub, JSCLASS_NO_OPTIONAL_MEMBERS }; #if JS_HAS_TOSOURCE #include "jsprf.h" static JSBool bool_toSource(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) { jsval v; char buf[32]; JSString *str; if (JSVAL_IS_BOOLEAN((jsval)obj)) { v = (jsval)obj; } else { if (!JS_InstanceOf(cx, obj, &js_BooleanClass, argv)) return JS_FALSE; v = OBJ_GET_SLOT(cx, obj, JSSLOT_PRIVATE); if (!JSVAL_IS_BOOLEAN(v)) return js_obj_toSource(cx, obj, argc, argv, rval); } JS_snprintf(buf, sizeof buf, "(new %s(%s))", js_BooleanClass.name, js_boolean_strs[JSVAL_TO_BOOLEAN(v) ? 1 : 0]); str = JS_NewStringCopyZ(cx, buf); if (!str) return JS_FALSE; *rval = STRING_TO_JSVAL(str); return JS_TRUE; } #endif static JSBool bool_toString(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) { jsval v; JSAtom *atom; JSString *str; if (JSVAL_IS_BOOLEAN((jsval)obj)) { v = (jsval)obj; } else { if (!JS_InstanceOf(cx, obj, &js_BooleanClass, argv)) return JS_FALSE; v = OBJ_GET_SLOT(cx, obj, JSSLOT_PRIVATE); if (!JSVAL_IS_BOOLEAN(v)) return js_obj_toString(cx, obj, argc, argv, rval); } atom = cx->runtime->atomState.booleanAtoms[JSVAL_TO_BOOLEAN(v) ? 1 : 0]; str = ATOM_TO_STRING(atom); if (!str) return JS_FALSE; *rval = STRING_TO_JSVAL(str); return JS_TRUE; } static JSBool bool_valueOf(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) { if (JSVAL_IS_BOOLEAN((jsval)obj)) { *rval = (jsval)obj; return JS_TRUE; } if (!JS_InstanceOf(cx, obj, &js_BooleanClass, argv)) return JS_FALSE; *rval = OBJ_GET_SLOT(cx, obj, JSSLOT_PRIVATE); return JS_TRUE; } static JSFunctionSpec boolean_methods[] = { #if JS_HAS_TOSOURCE {js_toSource_str, bool_toSource, 0,JSFUN_THISP_BOOLEAN,0}, #endif {js_toString_str, bool_toString, 0,JSFUN_THISP_BOOLEAN,0}, {js_valueOf_str, bool_valueOf, 0,JSFUN_THISP_BOOLEAN,0}, {0,0,0,0,0} }; static JSBool Boolean(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) { JSBool b; jsval bval; if (argc != 0) { if (!js_ValueToBoolean(cx, argv[0], &b)) return JS_FALSE; bval = BOOLEAN_TO_JSVAL(b); } else { bval = JSVAL_FALSE; } if (!(cx->fp->flags & JSFRAME_CONSTRUCTING)) { *rval = bval; return JS_TRUE; } OBJ_SET_SLOT(cx, obj, JSSLOT_PRIVATE, bval); return JS_TRUE; } JSObject * js_InitBooleanClass(JSContext *cx, JSObject *obj) { JSObject *proto; proto = JS_InitClass(cx, obj, NULL, &js_BooleanClass, Boolean, 1, NULL, boolean_methods, NULL, NULL); if (!proto) return NULL; OBJ_SET_SLOT(cx, proto, JSSLOT_PRIVATE, JSVAL_FALSE); return proto; } JSObject * js_BooleanToObject(JSContext *cx, JSBool b) { JSObject *obj; obj = js_NewObject(cx, &js_BooleanClass, NULL, NULL); if (!obj) return NULL; OBJ_SET_SLOT(cx, obj, JSSLOT_PRIVATE, BOOLEAN_TO_JSVAL(b)); return obj; } JSString * js_BooleanToString(JSContext *cx, JSBool b) { return ATOM_TO_STRING(cx->runtime->atomState.booleanAtoms[b ? 1 : 0]); } JSBool js_ValueToBoolean(JSContext *cx, jsval v, JSBool *bp) { JSBool b; jsdouble d; if (JSVAL_IS_NULL(v) || JSVAL_IS_VOID(v)) { b = JS_FALSE; } else if (JSVAL_IS_OBJECT(v)) { if (!JS_VERSION_IS_ECMA(cx)) { if (!OBJ_DEFAULT_VALUE(cx, JSVAL_TO_OBJECT(v), JSTYPE_BOOLEAN, &v)) return JS_FALSE; if (!JSVAL_IS_BOOLEAN(v)) v = JSVAL_TRUE; /* non-null object is true */ b = JSVAL_TO_BOOLEAN(v); } else { b = JS_TRUE; } } else if (JSVAL_IS_STRING(v)) { b = JSSTRING_LENGTH(JSVAL_TO_STRING(v)) ? JS_TRUE : JS_FALSE; } else if (JSVAL_IS_INT(v)) { b = JSVAL_TO_INT(v) ? JS_TRUE : JS_FALSE; } else if (JSVAL_IS_DOUBLE(v)) { d = *JSVAL_TO_DOUBLE(v); b = (!JSDOUBLE_IS_NaN(d) && d != 0) ? JS_TRUE : JS_FALSE; } else { JS_ASSERT(JSVAL_IS_BOOLEAN(v)); b = JSVAL_TO_BOOLEAN(v); } *bp = b; return JS_TRUE; } pacparser-1.4.5/src/spidermonkey/js/src/jsbool.h000066400000000000000000000052321464010763600216560ustar00rootroot00000000000000/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- * * ***** BEGIN LICENSE BLOCK ***** * Version: MPL 1.1/GPL 2.0/LGPL 2.1 * * The contents of this file are subject to the Mozilla Public License Version * 1.1 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * http://www.mozilla.org/MPL/ * * Software distributed under the License is distributed on an "AS IS" basis, * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License * for the specific language governing rights and limitations under the * License. * * The Original Code is Mozilla Communicator client code, released * March 31, 1998. * * The Initial Developer of the Original Code is * Netscape Communications Corporation. * Portions created by the Initial Developer are Copyright (C) 1998 * the Initial Developer. All Rights Reserved. * * Contributor(s): * * Alternatively, the contents of this file may be used under the terms of * either of the GNU General Public License Version 2 or later (the "GPL"), * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), * in which case the provisions of the GPL or the LGPL are applicable instead * of those above. If you wish to allow use of your version of this file only * under the terms of either the GPL or the LGPL, and not to allow others to * use your version of this file under the terms of the MPL, indicate your * decision by deleting the provisions above and replace them with the notice * and other provisions required by the GPL or the LGPL. If you do not delete * the provisions above, a recipient may use your version of this file under * the terms of any one of the MPL, the GPL or the LGPL. * * ***** END LICENSE BLOCK ***** */ #ifndef jsbool_h___ #define jsbool_h___ /* * JS boolean interface. */ JS_BEGIN_EXTERN_C /* * Crypto-booleans, not visible to script but used internally by the engine. * * JSVAL_HOLE is a useful value for identifying a hole in an array. It's also * used in the interpreter to represent "no exception pending". In general it * can be used to represent "no value". * * JSVAL_ARETURN is used to throw asynchronous return for generator.close(). */ #define JSVAL_HOLE BOOLEAN_TO_JSVAL(2) #define JSVAL_ARETURN BOOLEAN_TO_JSVAL(3) extern JSClass js_BooleanClass; extern JSObject * js_InitBooleanClass(JSContext *cx, JSObject *obj); extern JSObject * js_BooleanToObject(JSContext *cx, JSBool b); extern JSString * js_BooleanToString(JSContext *cx, JSBool b); extern JSBool js_ValueToBoolean(JSContext *cx, jsval v, JSBool *bp); JS_END_EXTERN_C #endif /* jsbool_h___ */ pacparser-1.4.5/src/spidermonkey/js/src/jsclist.h000066400000000000000000000100751464010763600220420ustar00rootroot00000000000000/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* ***** BEGIN LICENSE BLOCK ***** * Version: MPL 1.1/GPL 2.0/LGPL 2.1 * * The contents of this file are subject to the Mozilla Public License Version * 1.1 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * http://www.mozilla.org/MPL/ * * Software distributed under the License is distributed on an "AS IS" basis, * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License * for the specific language governing rights and limitations under the * License. * * The Original Code is Mozilla Communicator client code, released * March 31, 1998. * * The Initial Developer of the Original Code is * Netscape Communications Corporation. * Portions created by the Initial Developer are Copyright (C) 1998 * the Initial Developer. All Rights Reserved. * * Contributor(s): * * Alternatively, the contents of this file may be used under the terms of * either of the GNU General Public License Version 2 or later (the "GPL"), * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), * in which case the provisions of the GPL or the LGPL are applicable instead * of those above. If you wish to allow use of your version of this file only * under the terms of either the GPL or the LGPL, and not to allow others to * use your version of this file under the terms of the MPL, indicate your * decision by deleting the provisions above and replace them with the notice * and other provisions required by the GPL or the LGPL. If you do not delete * the provisions above, a recipient may use your version of this file under * the terms of any one of the MPL, the GPL or the LGPL. * * ***** END LICENSE BLOCK ***** */ #ifndef jsclist_h___ #define jsclist_h___ #include "jstypes.h" /* ** Circular linked list */ typedef struct JSCListStr { struct JSCListStr *next; struct JSCListStr *prev; } JSCList; /* ** Insert element "_e" into the list, before "_l". */ #define JS_INSERT_BEFORE(_e,_l) \ JS_BEGIN_MACRO \ (_e)->next = (_l); \ (_e)->prev = (_l)->prev; \ (_l)->prev->next = (_e); \ (_l)->prev = (_e); \ JS_END_MACRO /* ** Insert element "_e" into the list, after "_l". */ #define JS_INSERT_AFTER(_e,_l) \ JS_BEGIN_MACRO \ (_e)->next = (_l)->next; \ (_e)->prev = (_l); \ (_l)->next->prev = (_e); \ (_l)->next = (_e); \ JS_END_MACRO /* ** Return the element following element "_e" */ #define JS_NEXT_LINK(_e) \ ((_e)->next) /* ** Return the element preceding element "_e" */ #define JS_PREV_LINK(_e) \ ((_e)->prev) /* ** Append an element "_e" to the end of the list "_l" */ #define JS_APPEND_LINK(_e,_l) JS_INSERT_BEFORE(_e,_l) /* ** Insert an element "_e" at the head of the list "_l" */ #define JS_INSERT_LINK(_e,_l) JS_INSERT_AFTER(_e,_l) /* Return the head/tail of the list */ #define JS_LIST_HEAD(_l) (_l)->next #define JS_LIST_TAIL(_l) (_l)->prev /* ** Remove the element "_e" from it's circular list. */ #define JS_REMOVE_LINK(_e) \ JS_BEGIN_MACRO \ (_e)->prev->next = (_e)->next; \ (_e)->next->prev = (_e)->prev; \ JS_END_MACRO /* ** Remove the element "_e" from it's circular list. Also initializes the ** linkage. */ #define JS_REMOVE_AND_INIT_LINK(_e) \ JS_BEGIN_MACRO \ (_e)->prev->next = (_e)->next; \ (_e)->next->prev = (_e)->prev; \ (_e)->next = (_e); \ (_e)->prev = (_e); \ JS_END_MACRO /* ** Return non-zero if the given circular list "_l" is empty, zero if the ** circular list is not empty */ #define JS_CLIST_IS_EMPTY(_l) \ ((_l)->next == (_l)) /* ** Initialize a circular list */ #define JS_INIT_CLIST(_l) \ JS_BEGIN_MACRO \ (_l)->next = (_l); \ (_l)->prev = (_l); \ JS_END_MACRO #define JS_INIT_STATIC_CLIST(_l) \ {(_l), (_l)} #endif /* jsclist_h___ */ pacparser-1.4.5/src/spidermonkey/js/src/jscntxt.c000066400000000000000000001117511464010763600220620ustar00rootroot00000000000000/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- * vim: set ts=8 sw=4 et tw=80: * * ***** BEGIN LICENSE BLOCK ***** * Version: MPL 1.1/GPL 2.0/LGPL 2.1 * * The contents of this file are subject to the Mozilla Public License Version * 1.1 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * http://www.mozilla.org/MPL/ * * Software distributed under the License is distributed on an "AS IS" basis, * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License * for the specific language governing rights and limitations under the * License. * * The Original Code is Mozilla Communicator client code, released * March 31, 1998. * * The Initial Developer of the Original Code is * Netscape Communications Corporation. * Portions created by the Initial Developer are Copyright (C) 1998 * the Initial Developer. All Rights Reserved. * * Contributor(s): * * Alternatively, the contents of this file may be used under the terms of * either of the GNU General Public License Version 2 or later (the "GPL"), * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), * in which case the provisions of the GPL or the LGPL are applicable instead * of those above. If you wish to allow use of your version of this file only * under the terms of either the GPL or the LGPL, and not to allow others to * use your version of this file under the terms of the MPL, indicate your * decision by deleting the provisions above and replace them with the notice * and other provisions required by the GPL or the LGPL. If you do not delete * the provisions above, a recipient may use your version of this file under * the terms of any one of the MPL, the GPL or the LGPL. * * ***** END LICENSE BLOCK ***** */ /* * JS execution context. */ #include "jsstddef.h" #include #include #include #include "jstypes.h" #include "jsarena.h" /* Added by JSIFY */ #include "jsutil.h" /* Added by JSIFY */ #include "jsclist.h" #include "jsprf.h" #include "jsatom.h" #include "jscntxt.h" #include "jsconfig.h" #include "jsdbgapi.h" #include "jsexn.h" #include "jsgc.h" #include "jslock.h" #include "jsnum.h" #include "jsobj.h" #include "jsopcode.h" #include "jsscan.h" #include "jsscope.h" #include "jsscript.h" #include "jsstr.h" #ifdef JS_THREADSAFE /* * Callback function to delete a JSThread info when the thread that owns it * is destroyed. */ void JS_DLL_CALLBACK js_ThreadDestructorCB(void *ptr) { JSThread *thread = (JSThread *)ptr; if (!thread) return; while (!JS_CLIST_IS_EMPTY(&thread->contextList)) { /* NB: use a temporary, as the macro evaluates its args many times. */ JSCList *link = thread->contextList.next; JS_REMOVE_AND_INIT_LINK(link); } GSN_CACHE_CLEAR(&thread->gsnCache); free(thread); } /* * Get current thread-local JSThread info, creating one if it doesn't exist. * Each thread has a unique JSThread pointer. * * Since we are dealing with thread-local data, no lock is needed. * * Return a pointer to the thread local info, NULL if the system runs out * of memory, or it failed to set thread private data (neither case is very * likely; both are probably due to out-of-memory). It is up to the caller * to report an error, if possible. */ JSThread * js_GetCurrentThread(JSRuntime *rt) { JSThread *thread; thread = (JSThread *)PR_GetThreadPrivate(rt->threadTPIndex); if (!thread) { thread = (JSThread *) calloc(1, sizeof(JSThread)); if (!thread) return NULL; if (PR_FAILURE == PR_SetThreadPrivate(rt->threadTPIndex, thread)) { free(thread); return NULL; } JS_INIT_CLIST(&thread->contextList); thread->id = js_CurrentThreadId(); /* js_SetContextThread initialize gcFreeLists as necessary. */ #ifdef DEBUG memset(thread->gcFreeLists, JS_FREE_PATTERN, sizeof(thread->gcFreeLists)); #endif } return thread; } /* * Sets current thread as owning thread of a context by assigning the * thread-private info to the context. If the current thread doesn't have * private JSThread info, create one. */ JSBool js_SetContextThread(JSContext *cx) { JSThread *thread = js_GetCurrentThread(cx->runtime); if (!thread) { JS_ReportOutOfMemory(cx); return JS_FALSE; } /* * Clear gcFreeLists on each transition from 0 to 1 context active on the * current thread. See bug 351602. */ if (JS_CLIST_IS_EMPTY(&thread->contextList)) memset(thread->gcFreeLists, 0, sizeof(thread->gcFreeLists)); cx->thread = thread; JS_REMOVE_LINK(&cx->threadLinks); JS_APPEND_LINK(&cx->threadLinks, &thread->contextList); return JS_TRUE; } /* Remove the owning thread info of a context. */ void js_ClearContextThread(JSContext *cx) { JS_REMOVE_AND_INIT_LINK(&cx->threadLinks); #ifdef DEBUG if (JS_CLIST_IS_EMPTY(&cx->thread->contextList)) { memset(cx->thread->gcFreeLists, JS_FREE_PATTERN, sizeof(cx->thread->gcFreeLists)); } #endif cx->thread = NULL; } #endif /* JS_THREADSAFE */ void js_OnVersionChange(JSContext *cx) { #ifdef DEBUG JSVersion version = JSVERSION_NUMBER(cx); JS_ASSERT(version == JSVERSION_DEFAULT || version >= JSVERSION_ECMA_3); #endif } void js_SetVersion(JSContext *cx, JSVersion version) { cx->version = version; js_OnVersionChange(cx); } JSContext * js_NewContext(JSRuntime *rt, size_t stackChunkSize) { JSContext *cx; JSBool ok, first; JSContextCallback cxCallback; cx = (JSContext *) malloc(sizeof *cx); if (!cx) return NULL; memset(cx, 0, sizeof *cx); cx->runtime = rt; #if JS_STACK_GROWTH_DIRECTION > 0 cx->stackLimit = (jsuword)-1; #endif #ifdef JS_THREADSAFE JS_INIT_CLIST(&cx->threadLinks); js_SetContextThread(cx); #endif JS_LOCK_GC(rt); for (;;) { first = (rt->contextList.next == &rt->contextList); if (rt->state == JSRTS_UP) { JS_ASSERT(!first); break; } if (rt->state == JSRTS_DOWN) { JS_ASSERT(first); rt->state = JSRTS_LAUNCHING; break; } JS_WAIT_CONDVAR(rt->stateChange, JS_NO_TIMEOUT); } JS_APPEND_LINK(&cx->links, &rt->contextList); JS_UNLOCK_GC(rt); /* * First we do the infallible, every-time per-context initializations. * Should a later, fallible initialization (js_InitRegExpStatics, e.g., * or the stuff under 'if (first)' below) fail, at least the version * and arena-pools will be valid and safe to use (say, from the last GC * done by js_DestroyContext). */ cx->version = JSVERSION_DEFAULT; cx->jsop_eq = JSOP_EQ; cx->jsop_ne = JSOP_NE; JS_InitArenaPool(&cx->stackPool, "stack", stackChunkSize, sizeof(jsval)); JS_InitArenaPool(&cx->tempPool, "temp", 1024, sizeof(jsdouble)); if (!js_InitRegExpStatics(cx, &cx->regExpStatics)) { js_DestroyContext(cx, JSDCM_NEW_FAILED); return NULL; } /* * If cx is the first context on this runtime, initialize well-known atoms, * keywords, numbers, and strings. If one of these steps should fail, the * runtime will be left in a partially initialized state, with zeroes and * nulls stored in the default-initialized remainder of the struct. We'll * clean the runtime up under js_DestroyContext, because cx will be "last" * as well as "first". */ if (first) { #ifdef JS_THREADSAFE JS_BeginRequest(cx); #endif /* * Both atomState and the scriptFilenameTable may be left over from a * previous episode of non-zero contexts alive in rt, so don't re-init * either table if it's not necessary. Just repopulate atomState with * well-known internal atoms, and with the reserved identifiers added * by the scanner. */ ok = (rt->atomState.liveAtoms == 0) ? js_InitAtomState(cx, &rt->atomState) : js_InitPinnedAtoms(cx, &rt->atomState); if (ok && !rt->scriptFilenameTable) ok = js_InitRuntimeScriptState(rt); if (ok) ok = js_InitRuntimeNumberState(cx); if (ok) ok = js_InitRuntimeStringState(cx); #ifdef JS_THREADSAFE JS_EndRequest(cx); #endif if (!ok) { js_DestroyContext(cx, JSDCM_NEW_FAILED); return NULL; } JS_LOCK_GC(rt); rt->state = JSRTS_UP; JS_NOTIFY_ALL_CONDVAR(rt->stateChange); JS_UNLOCK_GC(rt); } cxCallback = rt->cxCallback; if (cxCallback && !cxCallback(cx, JSCONTEXT_NEW)) { js_DestroyContext(cx, JSDCM_NEW_FAILED); return NULL; } return cx; } void js_DestroyContext(JSContext *cx, JSDestroyContextMode mode) { JSRuntime *rt; JSContextCallback cxCallback; JSBool last; JSArgumentFormatMap *map; JSLocalRootStack *lrs; JSLocalRootChunk *lrc; rt = cx->runtime; if (mode != JSDCM_NEW_FAILED) { cxCallback = rt->cxCallback; if (cxCallback) { /* * JSCONTEXT_DESTROY callback is not allowed to fail and must * return true. */ #ifdef DEBUG JSBool callbackStatus = #endif cxCallback(cx, JSCONTEXT_DESTROY); JS_ASSERT(callbackStatus); } } /* Remove cx from context list first. */ JS_LOCK_GC(rt); JS_ASSERT(rt->state == JSRTS_UP || rt->state == JSRTS_LAUNCHING); JS_REMOVE_LINK(&cx->links); last = (rt->contextList.next == &rt->contextList); if (last) rt->state = JSRTS_LANDING; JS_UNLOCK_GC(rt); if (last) { #ifdef JS_THREADSAFE /* * If cx is not in a request already, begin one now so that we wait * for any racing GC started on a not-last context to finish, before * we plow ahead and unpin atoms. Note that even though we begin a * request here if necessary, we end all requests on cx below before * forcing a final GC. This lets any not-last context destruction * racing in another thread try to force or maybe run the GC, but by * that point, rt->state will not be JSRTS_UP, and that GC attempt * will return early. */ if (cx->requestDepth == 0) JS_BeginRequest(cx); #endif /* Unpin all pinned atoms before final GC. */ js_UnpinPinnedAtoms(&rt->atomState); /* Unlock and clear GC things held by runtime pointers. */ js_FinishRuntimeNumberState(cx); js_FinishRuntimeStringState(cx); /* Clear debugging state to remove GC roots. */ JS_ClearAllTraps(cx); JS_ClearAllWatchPoints(cx); } /* * Remove more GC roots in regExpStatics, then collect garbage. * XXX anti-modularity alert: we rely on the call to js_RemoveRoot within * XXX this function call to wait for any racing GC to complete, in the * XXX case where JS_DestroyContext is called outside of a request on cx */ js_FreeRegExpStatics(cx, &cx->regExpStatics); #ifdef JS_THREADSAFE /* * Destroying a context implicitly calls JS_EndRequest(). Also, we must * end our request here in case we are "last" -- in that event, another * js_DestroyContext that was not last might be waiting in the GC for our * request to end. We'll let it run below, just before we do the truly * final GC and then free atom state. * * At this point, cx must be inaccessible to other threads. It's off the * rt->contextList, and it should not be reachable via any object private * data structure. */ while (cx->requestDepth != 0) JS_EndRequest(cx); #endif if (last) { js_GC(cx, GC_LAST_CONTEXT); /* Try to free atom state, now that no unrooted scripts survive. */ if (rt->atomState.liveAtoms == 0) js_FreeAtomState(cx, &rt->atomState); /* Also free the script filename table if it exists and is empty. */ if (rt->scriptFilenameTable && rt->scriptFilenameTable->nentries == 0) js_FinishRuntimeScriptState(rt); /* * Free the deflated string cache, but only after the last GC has * collected all unleaked strings. */ js_FinishDeflatedStringCache(rt); /* Take the runtime down, now that it has no contexts or atoms. */ JS_LOCK_GC(rt); rt->state = JSRTS_DOWN; JS_NOTIFY_ALL_CONDVAR(rt->stateChange); JS_UNLOCK_GC(rt); } else { if (mode == JSDCM_FORCE_GC) js_GC(cx, GC_NORMAL); else if (mode == JSDCM_MAYBE_GC) JS_MaybeGC(cx); } /* Free the stuff hanging off of cx. */ JS_FinishArenaPool(&cx->stackPool); JS_FinishArenaPool(&cx->tempPool); if (cx->lastMessage) free(cx->lastMessage); /* Remove any argument formatters. */ map = cx->argumentFormatMap; while (map) { JSArgumentFormatMap *temp = map; map = map->next; JS_free(cx, temp); } /* Destroy the resolve recursion damper. */ if (cx->resolvingTable) { JS_DHashTableDestroy(cx->resolvingTable); cx->resolvingTable = NULL; } lrs = cx->localRootStack; if (lrs) { while ((lrc = lrs->topChunk) != &lrs->firstChunk) { lrs->topChunk = lrc->down; JS_free(cx, lrc); } JS_free(cx, lrs); } #ifdef JS_THREADSAFE js_ClearContextThread(cx); #endif /* Finally, free cx itself. */ free(cx); } JSBool js_ValidContextPointer(JSRuntime *rt, JSContext *cx) { JSCList *cl; for (cl = rt->contextList.next; cl != &rt->contextList; cl = cl->next) { if (cl == &cx->links) return JS_TRUE; } JS_RUNTIME_METER(rt, deadContexts); return JS_FALSE; } JSContext * js_ContextIterator(JSRuntime *rt, JSBool unlocked, JSContext **iterp) { JSContext *cx = *iterp; if (unlocked) JS_LOCK_GC(rt); if (!cx) cx = (JSContext *)&rt->contextList; cx = (JSContext *)cx->links.next; if (&cx->links == &rt->contextList) cx = NULL; *iterp = cx; if (unlocked) JS_UNLOCK_GC(rt); return cx; } JS_STATIC_DLL_CALLBACK(const void *) resolving_GetKey(JSDHashTable *table, JSDHashEntryHdr *hdr) { JSResolvingEntry *entry = (JSResolvingEntry *)hdr; return &entry->key; } JS_STATIC_DLL_CALLBACK(JSDHashNumber) resolving_HashKey(JSDHashTable *table, const void *ptr) { const JSResolvingKey *key = (const JSResolvingKey *)ptr; return ((JSDHashNumber)JS_PTR_TO_UINT32(key->obj) >> JSVAL_TAGBITS) ^ key->id; } JS_PUBLIC_API(JSBool) resolving_MatchEntry(JSDHashTable *table, const JSDHashEntryHdr *hdr, const void *ptr) { const JSResolvingEntry *entry = (const JSResolvingEntry *)hdr; const JSResolvingKey *key = (const JSResolvingKey *)ptr; return entry->key.obj == key->obj && entry->key.id == key->id; } static const JSDHashTableOps resolving_dhash_ops = { JS_DHashAllocTable, JS_DHashFreeTable, resolving_GetKey, resolving_HashKey, resolving_MatchEntry, JS_DHashMoveEntryStub, JS_DHashClearEntryStub, JS_DHashFinalizeStub, NULL }; JSBool js_StartResolving(JSContext *cx, JSResolvingKey *key, uint32 flag, JSResolvingEntry **entryp) { JSDHashTable *table; JSResolvingEntry *entry; table = cx->resolvingTable; if (!table) { table = JS_NewDHashTable(&resolving_dhash_ops, NULL, sizeof(JSResolvingEntry), JS_DHASH_MIN_SIZE); if (!table) goto outofmem; cx->resolvingTable = table; } entry = (JSResolvingEntry *) JS_DHashTableOperate(table, key, JS_DHASH_ADD); if (!entry) goto outofmem; if (entry->flags & flag) { /* An entry for (key, flag) exists already -- dampen recursion. */ entry = NULL; } else { /* Fill in key if we were the first to add entry, then set flag. */ if (!entry->key.obj) entry->key = *key; entry->flags |= flag; } *entryp = entry; return JS_TRUE; outofmem: JS_ReportOutOfMemory(cx); return JS_FALSE; } void js_StopResolving(JSContext *cx, JSResolvingKey *key, uint32 flag, JSResolvingEntry *entry, uint32 generation) { JSDHashTable *table; /* * Clear flag from entry->flags and return early if other flags remain. * We must take care to re-lookup entry if the table has changed since * it was found by js_StartResolving. */ table = cx->resolvingTable; if (!entry || table->generation != generation) { entry = (JSResolvingEntry *) JS_DHashTableOperate(table, key, JS_DHASH_LOOKUP); } JS_ASSERT(JS_DHASH_ENTRY_IS_BUSY(&entry->hdr)); entry->flags &= ~flag; if (entry->flags) return; /* * Do a raw remove only if fewer entries were removed than would cause * alpha to be less than .5 (alpha is at most .75). Otherwise, we just * call JS_DHashTableOperate to re-lookup the key and remove its entry, * compressing or shrinking the table as needed. */ if (table->removedCount < JS_DHASH_TABLE_SIZE(table) >> 2) JS_DHashTableRawRemove(table, &entry->hdr); else JS_DHashTableOperate(table, key, JS_DHASH_REMOVE); } JSBool js_EnterLocalRootScope(JSContext *cx) { JSLocalRootStack *lrs; int mark; lrs = cx->localRootStack; if (!lrs) { lrs = (JSLocalRootStack *) JS_malloc(cx, sizeof *lrs); if (!lrs) return JS_FALSE; lrs->scopeMark = JSLRS_NULL_MARK; lrs->rootCount = 0; lrs->topChunk = &lrs->firstChunk; lrs->firstChunk.down = NULL; cx->localRootStack = lrs; } /* Push lrs->scopeMark to save it for restore when leaving. */ mark = js_PushLocalRoot(cx, lrs, INT_TO_JSVAL(lrs->scopeMark)); if (mark < 0) return JS_FALSE; lrs->scopeMark = (uint32) mark; return JS_TRUE; } void js_LeaveLocalRootScopeWithResult(JSContext *cx, jsval rval) { JSLocalRootStack *lrs; uint32 mark, m, n; JSLocalRootChunk *lrc; /* Defend against buggy native callers. */ lrs = cx->localRootStack; JS_ASSERT(lrs && lrs->rootCount != 0); if (!lrs || lrs->rootCount == 0) return; mark = lrs->scopeMark; JS_ASSERT(mark != JSLRS_NULL_MARK); if (mark == JSLRS_NULL_MARK) return; /* Free any chunks being popped by this leave operation. */ m = mark >> JSLRS_CHUNK_SHIFT; n = (lrs->rootCount - 1) >> JSLRS_CHUNK_SHIFT; while (n > m) { lrc = lrs->topChunk; JS_ASSERT(lrc != &lrs->firstChunk); lrs->topChunk = lrc->down; JS_free(cx, lrc); --n; } /* * Pop the scope, restoring lrs->scopeMark. If rval is a GC-thing, push * it on the caller's scope, or store it in lastInternalResult if we are * leaving the outermost scope. We don't need to allocate a new lrc * because we can overwrite the old mark's slot with rval. */ lrc = lrs->topChunk; m = mark & JSLRS_CHUNK_MASK; lrs->scopeMark = (uint32) JSVAL_TO_INT(lrc->roots[m]); if (JSVAL_IS_GCTHING(rval) && !JSVAL_IS_NULL(rval)) { if (mark == 0) { cx->weakRoots.lastInternalResult = rval; } else { /* * Increment m to avoid the "else if (m == 0)" case below. If * rval is not a GC-thing, that case would take care of freeing * any chunk that contained only the old mark. Since rval *is* * a GC-thing here, we want to reuse that old mark's slot. */ lrc->roots[m++] = rval; ++mark; } } lrs->rootCount = (uint32) mark; /* * Free the stack eagerly, risking malloc churn. The alternative would * require an lrs->entryCount member, maintained by Enter and Leave, and * tested by the GC in addition to the cx->localRootStack non-null test. * * That approach would risk hoarding 264 bytes (net) per context. Right * now it seems better to give fresh (dirty in CPU write-back cache, and * the data is no longer needed) memory back to the malloc heap. */ if (mark == 0) { cx->localRootStack = NULL; JS_free(cx, lrs); } else if (m == 0) { lrs->topChunk = lrc->down; JS_free(cx, lrc); } } void js_ForgetLocalRoot(JSContext *cx, jsval v) { JSLocalRootStack *lrs; uint32 i, j, m, n, mark; JSLocalRootChunk *lrc, *lrc2; jsval top; lrs = cx->localRootStack; JS_ASSERT(lrs && lrs->rootCount); if (!lrs || lrs->rootCount == 0) return; /* Prepare to pop the top-most value from the stack. */ n = lrs->rootCount - 1; m = n & JSLRS_CHUNK_MASK; lrc = lrs->topChunk; top = lrc->roots[m]; /* Be paranoid about calls on an empty scope. */ mark = lrs->scopeMark; JS_ASSERT(mark < n); if (mark >= n) return; /* If v was not the last root pushed in the top scope, find it. */ if (top != v) { /* Search downward in case v was recently pushed. */ i = n; j = m; lrc2 = lrc; while (--i > mark) { if (j == 0) lrc2 = lrc2->down; j = i & JSLRS_CHUNK_MASK; if (lrc2->roots[j] == v) break; } /* If we didn't find v in this scope, assert and bail out. */ JS_ASSERT(i != mark); if (i == mark) return; /* Swap top and v so common tail code can pop v. */ lrc2->roots[j] = top; } /* Pop the last value from the stack. */ lrc->roots[m] = JSVAL_NULL; lrs->rootCount = n; if (m == 0) { JS_ASSERT(n != 0); JS_ASSERT(lrc != &lrs->firstChunk); lrs->topChunk = lrc->down; JS_free(cx, lrc); } } int js_PushLocalRoot(JSContext *cx, JSLocalRootStack *lrs, jsval v) { uint32 n, m; JSLocalRootChunk *lrc; n = lrs->rootCount; m = n & JSLRS_CHUNK_MASK; if (n == 0 || m != 0) { /* * At start of first chunk, or not at start of a non-first top chunk. * Check for lrs->rootCount overflow. */ if ((uint32)(n + 1) == 0) { JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_TOO_MANY_LOCAL_ROOTS); return -1; } lrc = lrs->topChunk; JS_ASSERT(n != 0 || lrc == &lrs->firstChunk); } else { /* * After lrs->firstChunk, trying to index at a power-of-two chunk * boundary: need a new chunk. */ lrc = (JSLocalRootChunk *) JS_malloc(cx, sizeof *lrc); if (!lrc) return -1; lrc->down = lrs->topChunk; lrs->topChunk = lrc; } lrs->rootCount = n + 1; lrc->roots[m] = v; return (int) n; } void js_MarkLocalRoots(JSContext *cx, JSLocalRootStack *lrs) { uint32 n, m, mark; JSLocalRootChunk *lrc; n = lrs->rootCount; if (n == 0) return; mark = lrs->scopeMark; lrc = lrs->topChunk; do { while (--n > mark) { #ifdef GC_MARK_DEBUG char name[22]; JS_snprintf(name, sizeof name, "", n); #endif m = n & JSLRS_CHUNK_MASK; JS_ASSERT(JSVAL_IS_GCTHING(lrc->roots[m])); GC_MARK(cx, JSVAL_TO_GCTHING(lrc->roots[m]), name); if (m == 0) lrc = lrc->down; } m = n & JSLRS_CHUNK_MASK; mark = JSVAL_TO_INT(lrc->roots[m]); if (m == 0) lrc = lrc->down; } while (n != 0); JS_ASSERT(!lrc); } static void ReportError(JSContext *cx, const char *message, JSErrorReport *reportp) { /* * Check the error report, and set a JavaScript-catchable exception * if the error is defined to have an associated exception. If an * exception is thrown, then the JSREPORT_EXCEPTION flag will be set * on the error report, and exception-aware hosts should ignore it. */ JS_ASSERT(reportp); if (reportp->errorNumber == JSMSG_UNCAUGHT_EXCEPTION) reportp->flags |= JSREPORT_EXCEPTION; /* * Call the error reporter only if an exception wasn't raised. * * If an exception was raised, then we call the debugErrorHook * (if present) to give it a chance to see the error before it * propagates out of scope. This is needed for compatability * with the old scheme. */ if (!js_ErrorToException(cx, message, reportp)) { js_ReportErrorAgain(cx, message, reportp); } else if (cx->runtime->debugErrorHook && cx->errorReporter) { JSDebugErrorHook hook = cx->runtime->debugErrorHook; /* test local in case debugErrorHook changed on another thread */ if (hook) hook(cx, message, reportp, cx->runtime->debugErrorHookData); } } /* * We don't post an exception in this case, since doing so runs into * complications of pre-allocating an exception object which required * running the Exception class initializer early etc. * Instead we just invoke the errorReporter with an "Out Of Memory" * type message, and then hope the process ends swiftly. */ void js_ReportOutOfMemory(JSContext *cx) { JSStackFrame *fp; JSErrorReport report; JSErrorReporter onError = cx->errorReporter; /* Get the message for this error, but we won't expand any arguments. */ const JSErrorFormatString *efs = js_GetLocalizedErrorMessage(cx, NULL, NULL, JSMSG_OUT_OF_MEMORY); const char *msg = efs ? efs->format : "Out of memory"; /* Fill out the report, but don't do anything that requires allocation. */ memset(&report, 0, sizeof (struct JSErrorReport)); report.flags = JSREPORT_ERROR; report.errorNumber = JSMSG_OUT_OF_MEMORY; /* * Walk stack until we find a frame that is associated with some script * rather than a native frame. */ for (fp = cx->fp; fp; fp = fp->down) { if (fp->script && fp->pc) { report.filename = fp->script->filename; report.lineno = js_PCToLineNumber(cx, fp->script, fp->pc); break; } } /* * If debugErrorHook is present then we give it a chance to veto * sending the error on to the regular ErrorReporter. */ if (onError) { JSDebugErrorHook hook = cx->runtime->debugErrorHook; if (hook && !hook(cx, msg, &report, cx->runtime->debugErrorHookData)) { onError = NULL; } } if (onError) onError(cx, msg, &report); } JSBool js_ReportErrorVA(JSContext *cx, uintN flags, const char *format, va_list ap) { char *message; jschar *ucmessage; size_t messagelen; JSStackFrame *fp; JSErrorReport report; JSBool warning; if ((flags & JSREPORT_STRICT) && !JS_HAS_STRICT_OPTION(cx)) return JS_TRUE; message = JS_vsmprintf(format, ap); if (!message) return JS_FALSE; messagelen = strlen(message); memset(&report, 0, sizeof (struct JSErrorReport)); report.flags = flags; report.errorNumber = JSMSG_USER_DEFINED_ERROR; report.ucmessage = ucmessage = js_InflateString(cx, message, &messagelen); /* Find the top-most active script frame, for best line number blame. */ for (fp = cx->fp; fp; fp = fp->down) { if (fp->script && fp->pc) { report.filename = fp->script->filename; report.lineno = js_PCToLineNumber(cx, fp->script, fp->pc); break; } } warning = JSREPORT_IS_WARNING(report.flags); if (warning && JS_HAS_WERROR_OPTION(cx)) { report.flags &= ~JSREPORT_WARNING; warning = JS_FALSE; } ReportError(cx, message, &report); free(message); JS_free(cx, ucmessage); return warning; } /* * The arguments from ap need to be packaged up into an array and stored * into the report struct. * * The format string addressed by the error number may contain operands * identified by the format {N}, where N is a decimal digit. Each of these * is to be replaced by the Nth argument from the va_list. The complete * message is placed into reportp->ucmessage converted to a JSString. * * Returns true if the expansion succeeds (can fail if out of memory). */ JSBool js_ExpandErrorArguments(JSContext *cx, JSErrorCallback callback, void *userRef, const uintN errorNumber, char **messagep, JSErrorReport *reportp, JSBool *warningp, JSBool charArgs, va_list ap) { const JSErrorFormatString *efs; int i; int argCount; *warningp = JSREPORT_IS_WARNING(reportp->flags); if (*warningp && JS_HAS_WERROR_OPTION(cx)) { reportp->flags &= ~JSREPORT_WARNING; *warningp = JS_FALSE; } *messagep = NULL; /* Most calls supply js_GetErrorMessage; if this is so, assume NULL. */ if (!callback || callback == js_GetErrorMessage) efs = js_GetLocalizedErrorMessage(cx, userRef, NULL, errorNumber); else efs = callback(userRef, NULL, errorNumber); if (efs) { size_t totalArgsLength = 0; size_t argLengths[10]; /* only {0} thru {9} supported */ argCount = efs->argCount; JS_ASSERT(argCount <= 10); if (argCount > 0) { /* * Gather the arguments into an array, and accumulate * their sizes. We allocate 1 more than necessary and * null it out to act as the caboose when we free the * pointers later. */ reportp->messageArgs = (const jschar **) JS_malloc(cx, sizeof(jschar *) * (argCount + 1)); if (!reportp->messageArgs) return JS_FALSE; reportp->messageArgs[argCount] = NULL; for (i = 0; i < argCount; i++) { if (charArgs) { char *charArg = va_arg(ap, char *); size_t charArgLength = strlen(charArg); reportp->messageArgs[i] = js_InflateString(cx, charArg, &charArgLength); if (!reportp->messageArgs[i]) goto error; } else { reportp->messageArgs[i] = va_arg(ap, jschar *); } argLengths[i] = js_strlen(reportp->messageArgs[i]); totalArgsLength += argLengths[i]; } /* NULL-terminate for easy copying. */ reportp->messageArgs[i] = NULL; } /* * Parse the error format, substituting the argument X * for {X} in the format. */ if (argCount > 0) { if (efs->format) { jschar *buffer, *fmt, *out; int expandedArgs = 0; size_t expandedLength; size_t len = strlen(efs->format); buffer = fmt = js_InflateString (cx, efs->format, &len); if (!buffer) goto error; expandedLength = len - (3 * argCount) /* exclude the {n} */ + totalArgsLength; /* * Note - the above calculation assumes that each argument * is used once and only once in the expansion !!! */ reportp->ucmessage = out = (jschar *) JS_malloc(cx, (expandedLength + 1) * sizeof(jschar)); if (!out) { JS_free (cx, buffer); goto error; } while (*fmt) { if (*fmt == '{') { if (isdigit(fmt[1])) { int d = JS7_UNDEC(fmt[1]); JS_ASSERT(d < argCount); js_strncpy(out, reportp->messageArgs[d], argLengths[d]); out += argLengths[d]; fmt += 3; expandedArgs++; continue; } } *out++ = *fmt++; } JS_ASSERT(expandedArgs == argCount); *out = 0; JS_free (cx, buffer); *messagep = js_DeflateString(cx, reportp->ucmessage, (size_t)(out - reportp->ucmessage)); if (!*messagep) goto error; } } else { /* * Zero arguments: the format string (if it exists) is the * entire message. */ if (efs->format) { size_t len; *messagep = JS_strdup(cx, efs->format); if (!*messagep) goto error; len = strlen(*messagep); reportp->ucmessage = js_InflateString(cx, *messagep, &len); if (!reportp->ucmessage) goto error; } } } if (*messagep == NULL) { /* where's the right place for this ??? */ const char *defaultErrorMessage = "No error message available for error number %d"; size_t nbytes = strlen(defaultErrorMessage) + 16; *messagep = (char *)JS_malloc(cx, nbytes); if (!*messagep) goto error; JS_snprintf(*messagep, nbytes, defaultErrorMessage, errorNumber); } return JS_TRUE; error: if (reportp->messageArgs) { /* free the arguments only if we allocated them */ if (charArgs) { i = 0; while (reportp->messageArgs[i]) JS_free(cx, (void *)reportp->messageArgs[i++]); } JS_free(cx, (void *)reportp->messageArgs); reportp->messageArgs = NULL; } if (reportp->ucmessage) { JS_free(cx, (void *)reportp->ucmessage); reportp->ucmessage = NULL; } if (*messagep) { JS_free(cx, (void *)*messagep); *messagep = NULL; } return JS_FALSE; } JSBool js_ReportErrorNumberVA(JSContext *cx, uintN flags, JSErrorCallback callback, void *userRef, const uintN errorNumber, JSBool charArgs, va_list ap) { JSStackFrame *fp; JSErrorReport report; char *message; JSBool warning; if ((flags & JSREPORT_STRICT) && !JS_HAS_STRICT_OPTION(cx)) return JS_TRUE; memset(&report, 0, sizeof (struct JSErrorReport)); report.flags = flags; report.errorNumber = errorNumber; /* * If we can't find out where the error was based on the current frame, * see if the next frame has a script/pc combo we can use. */ for (fp = cx->fp; fp; fp = fp->down) { if (fp->script && fp->pc) { report.filename = fp->script->filename; report.lineno = js_PCToLineNumber(cx, fp->script, fp->pc); break; } } if (!js_ExpandErrorArguments(cx, callback, userRef, errorNumber, &message, &report, &warning, charArgs, ap)) { return JS_FALSE; } ReportError(cx, message, &report); if (message) JS_free(cx, message); if (report.messageArgs) { /* * js_ExpandErrorArguments owns its messageArgs only if it had to * inflate the arguments (from regular |char *|s). */ if (charArgs) { int i = 0; while (report.messageArgs[i]) JS_free(cx, (void *)report.messageArgs[i++]); } JS_free(cx, (void *)report.messageArgs); } if (report.ucmessage) JS_free(cx, (void *)report.ucmessage); return warning; } JS_FRIEND_API(void) js_ReportErrorAgain(JSContext *cx, const char *message, JSErrorReport *reportp) { JSErrorReporter onError; if (!message) return; if (cx->lastMessage) free(cx->lastMessage); cx->lastMessage = JS_strdup(cx, message); if (!cx->lastMessage) return; onError = cx->errorReporter; /* * If debugErrorHook is present then we give it a chance to veto * sending the error on to the regular ErrorReporter. */ if (onError) { JSDebugErrorHook hook = cx->runtime->debugErrorHook; if (hook && !hook(cx, cx->lastMessage, reportp, cx->runtime->debugErrorHookData)) { onError = NULL; } } if (onError) onError(cx, cx->lastMessage, reportp); } void js_ReportIsNotDefined(JSContext *cx, const char *name) { JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_NOT_DEFINED, name); } #if defined DEBUG && defined XP_UNIX /* For gdb usage. */ void js_traceon(JSContext *cx) { cx->tracefp = stderr; } void js_traceoff(JSContext *cx) { cx->tracefp = NULL; } #endif JSErrorFormatString js_ErrorFormatString[JSErr_Limit] = { #define MSG_DEF(name, number, count, exception, format) \ { format, count, exception } , #include "js.msg" #undef MSG_DEF }; const JSErrorFormatString * js_GetErrorMessage(void *userRef, const char *locale, const uintN errorNumber) { if ((errorNumber > 0) && (errorNumber < JSErr_Limit)) return &js_ErrorFormatString[errorNumber]; return NULL; } pacparser-1.4.5/src/spidermonkey/js/src/jscntxt.h000066400000000000000000001114711464010763600220660ustar00rootroot00000000000000/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- * vim: set ts=8 sw=4 et tw=78: * * ***** BEGIN LICENSE BLOCK ***** * Version: MPL 1.1/GPL 2.0/LGPL 2.1 * * The contents of this file are subject to the Mozilla Public License Version * 1.1 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * http://www.mozilla.org/MPL/ * * Software distributed under the License is distributed on an "AS IS" basis, * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License * for the specific language governing rights and limitations under the * License. * * The Original Code is Mozilla Communicator client code, released * March 31, 1998. * * The Initial Developer of the Original Code is * Netscape Communications Corporation. * Portions created by the Initial Developer are Copyright (C) 1998 * the Initial Developer. All Rights Reserved. * * Contributor(s): * * Alternatively, the contents of this file may be used under the terms of * either of the GNU General Public License Version 2 or later (the "GPL"), * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), * in which case the provisions of the GPL or the LGPL are applicable instead * of those above. If you wish to allow use of your version of this file only * under the terms of either the GPL or the LGPL, and not to allow others to * use your version of this file under the terms of the MPL, indicate your * decision by deleting the provisions above and replace them with the notice * and other provisions required by the GPL or the LGPL. If you do not delete * the provisions above, a recipient may use your version of this file under * the terms of any one of the MPL, the GPL or the LGPL. * * ***** END LICENSE BLOCK ***** */ #ifndef jscntxt_h___ #define jscntxt_h___ /* * JS execution context. */ #include "jsarena.h" /* Added by JSIFY */ #include "jsclist.h" #include "jslong.h" #include "jsatom.h" #include "jsconfig.h" #include "jsdhash.h" #include "jsgc.h" #include "jsinterp.h" #include "jsobj.h" #include "jsprvtd.h" #include "jspubtd.h" #include "jsregexp.h" #include "jsutil.h" JS_BEGIN_EXTERN_C /* * js_GetSrcNote cache to avoid O(n^2) growth in finding a source note for a * given pc in a script. */ typedef struct JSGSNCache { JSScript *script; JSDHashTable table; #ifdef JS_GSNMETER uint32 hits; uint32 misses; uint32 fills; uint32 clears; # define GSN_CACHE_METER(cache,cnt) (++(cache)->cnt) #else # define GSN_CACHE_METER(cache,cnt) /* nothing */ #endif } JSGSNCache; #define GSN_CACHE_CLEAR(cache) \ JS_BEGIN_MACRO \ (cache)->script = NULL; \ if ((cache)->table.ops) { \ JS_DHashTableFinish(&(cache)->table); \ (cache)->table.ops = NULL; \ } \ GSN_CACHE_METER(cache, clears); \ JS_END_MACRO /* These helper macros take a cx as parameter and operate on its GSN cache. */ #define JS_CLEAR_GSN_CACHE(cx) GSN_CACHE_CLEAR(&JS_GSN_CACHE(cx)) #define JS_METER_GSN_CACHE(cx,cnt) GSN_CACHE_METER(&JS_GSN_CACHE(cx), cnt) #ifdef JS_THREADSAFE /* * Structure uniquely representing a thread. It holds thread-private data * that can be accessed without a global lock. */ struct JSThread { /* Linked list of all contexts active on this thread. */ JSCList contextList; /* Opaque thread-id, from NSPR's PR_GetCurrentThread(). */ jsword id; /* Thread-local gc free lists array. */ JSGCThing *gcFreeLists[GC_NUM_FREELISTS]; /* * Thread-local version of JSRuntime.gcMallocBytes to avoid taking * locks on each JS_malloc. */ uint32 gcMallocBytes; #if JS_HAS_GENERATORS /* Flag indicating that the current thread is executing close hooks. */ JSBool gcRunningCloseHooks; #endif /* * Store the GSN cache in struct JSThread, not struct JSContext, both to * save space and to simplify cleanup in js_GC. Any embedding (Firefox * or another Gecko application) that uses many contexts per thread is * unlikely to interleave js_GetSrcNote-intensive loops in the decompiler * among two or more contexts running script in one thread. */ JSGSNCache gsnCache; }; #define JS_GSN_CACHE(cx) ((cx)->thread->gsnCache) extern void JS_DLL_CALLBACK js_ThreadDestructorCB(void *ptr); extern JSBool js_SetContextThread(JSContext *cx); extern void js_ClearContextThread(JSContext *cx); extern JSThread * js_GetCurrentThread(JSRuntime *rt); #endif /* JS_THREADSAFE */ typedef enum JSDestroyContextMode { JSDCM_NO_GC, JSDCM_MAYBE_GC, JSDCM_FORCE_GC, JSDCM_NEW_FAILED } JSDestroyContextMode; typedef enum JSRuntimeState { JSRTS_DOWN, JSRTS_LAUNCHING, JSRTS_UP, JSRTS_LANDING } JSRuntimeState; typedef struct JSPropertyTreeEntry { JSDHashEntryHdr hdr; JSScopeProperty *child; } JSPropertyTreeEntry; /* * Forward declaration for opaque JSRuntime.nativeIteratorStates. */ typedef struct JSNativeIteratorState JSNativeIteratorState; struct JSRuntime { /* Runtime state, synchronized by the stateChange/gcLock condvar/lock. */ JSRuntimeState state; /* Context create/destroy callback. */ JSContextCallback cxCallback; /* Garbage collector state, used by jsgc.c. */ JSGCArenaList gcArenaList[GC_NUM_FREELISTS]; JSDHashTable gcRootsHash; JSDHashTable *gcLocksHash; jsrefcount gcKeepAtoms; uint32 gcBytes; uint32 gcLastBytes; uint32 gcMaxBytes; uint32 gcMaxMallocBytes; uint32 gcLevel; uint32 gcNumber; /* * NB: do not pack another flag here by claiming gcPadding unless the new * flag is written only by the GC thread. Atomic updates to packed bytes * are not guaranteed, so stores issued by one thread may be lost due to * unsynchronized read-modify-write cycles on other threads. */ JSPackedBool gcPoke; JSPackedBool gcRunning; uint16 gcPadding; JSGCCallback gcCallback; uint32 gcMallocBytes; JSGCArena *gcUnscannedArenaStackTop; #ifdef DEBUG size_t gcUnscannedBagSize; #endif /* * API compatibility requires keeping GCX_PRIVATE bytes separate from the * original GC types' byte tally. Otherwise embeddings that configure a * good limit for pre-GCX_PRIVATE versions of the engine will see memory * over-pressure too often, possibly leading to failed last-ditch GCs. * * The new XML GC-thing types do add to gcBytes, and they're larger than * the original GC-thing type size (8 bytes on most architectures). So a * user who enables E4X may want to increase the maxbytes value passed to * JS_NewRuntime. TODO: Note this in the API docs. */ uint32 gcPrivateBytes; /* * Table for tracking iterators to ensure that we close iterator's state * before finalizing the iterable object. */ JSPtrTable gcIteratorTable; #if JS_HAS_GENERATORS /* Runtime state to support close hooks. */ JSGCCloseState gcCloseState; #endif #ifdef JS_GCMETER JSGCStats gcStats; #endif /* Literal table maintained by jsatom.c functions. */ JSAtomState atomState; /* Random number generator state, used by jsmath.c. */ JSBool rngInitialized; int64 rngMultiplier; int64 rngAddend; int64 rngMask; int64 rngSeed; jsdouble rngDscale; /* Well-known numbers held for use by this runtime's contexts. */ jsdouble *jsNaN; jsdouble *jsNegativeInfinity; jsdouble *jsPositiveInfinity; #ifdef JS_THREADSAFE JSLock *deflatedStringCacheLock; #endif JSHashTable *deflatedStringCache; #ifdef DEBUG uint32 deflatedStringCacheBytes; #endif /* Empty string held for use by this runtime's contexts. */ JSString *emptyString; /* List of active contexts sharing this runtime; protected by gcLock. */ JSCList contextList; /* These are used for debugging -- see jsprvtd.h and jsdbgapi.h. */ JSTrapHandler interruptHandler; void *interruptHandlerData; JSNewScriptHook newScriptHook; void *newScriptHookData; JSDestroyScriptHook destroyScriptHook; void *destroyScriptHookData; JSTrapHandler debuggerHandler; void *debuggerHandlerData; JSSourceHandler sourceHandler; void *sourceHandlerData; JSInterpreterHook executeHook; void *executeHookData; JSInterpreterHook callHook; void *callHookData; JSObjectHook objectHook; void *objectHookData; JSTrapHandler throwHook; void *throwHookData; JSDebugErrorHook debugErrorHook; void *debugErrorHookData; /* More debugging state, see jsdbgapi.c. */ JSCList trapList; JSCList watchPointList; /* Weak links to properties, indexed by quickened get/set opcodes. */ /* XXX must come after JSCLists or MSVC alignment bug bites empty lists */ JSPropertyCache propertyCache; /* Client opaque pointer */ void *data; #ifdef JS_THREADSAFE /* These combine to interlock the GC and new requests. */ PRLock *gcLock; PRCondVar *gcDone; PRCondVar *requestDone; uint32 requestCount; JSThread *gcThread; /* Lock and owning thread pointer for JS_LOCK_RUNTIME. */ PRLock *rtLock; #ifdef DEBUG jsword rtLockOwner; #endif /* Used to synchronize down/up state change; protected by gcLock. */ PRCondVar *stateChange; /* Used to serialize cycle checks when setting __proto__ or __parent__. */ PRLock *setSlotLock; PRCondVar *setSlotDone; JSBool setSlotBusy; JSScope *setSlotScope; /* deadlock avoidance, see jslock.c */ /* * State for sharing single-threaded scopes, once a second thread tries to * lock a scope. The scopeSharingDone condvar is protected by rt->gcLock, * to minimize number of locks taken in JS_EndRequest. * * The scopeSharingTodo linked list is likewise "global" per runtime, not * one-list-per-context, to conserve space over all contexts, optimizing * for the likely case that scopes become shared rarely, and among a very * small set of threads (contexts). */ PRCondVar *scopeSharingDone; JSScope *scopeSharingTodo; /* * Magic terminator for the rt->scopeSharingTodo linked list, threaded through * scope->u.link. This hack allows us to test whether a scope is on the list * by asking whether scope->u.link is non-null. We use a large, likely bogus * pointer here to distinguish this value from any valid u.count (small int) * value. */ #define NO_SCOPE_SHARING_TODO ((JSScope *) 0xfeedbeef) /* * The index for JSThread info, returned by PR_NewThreadPrivateIndex. * The value is visible and shared by all threads, but the data is * private to each thread. */ PRUintn threadTPIndex; #endif /* JS_THREADSAFE */ /* * Check property accessibility for objects of arbitrary class. Used at * present to check f.caller accessibility for any function object f. */ JSCheckAccessOp checkObjectAccess; /* Security principals serialization support. */ JSPrincipalsTranscoder principalsTranscoder; /* Optional hook to find principals for an object in this runtime. */ JSObjectPrincipalsFinder findObjectPrincipals; /* * Shared scope property tree, and arena-pool for allocating its nodes. * The propertyRemovals counter is incremented for every js_ClearScope, * and for each js_RemoveScopeProperty that frees a slot in an object. * See js_NativeGet and js_NativeSet in jsobj.c. */ JSDHashTable propertyTreeHash; JSScopeProperty *propertyFreeList; JSArenaPool propertyArenaPool; int32 propertyRemovals; /* Script filename table. */ struct JSHashTable *scriptFilenameTable; JSCList scriptFilenamePrefixes; #ifdef JS_THREADSAFE PRLock *scriptFilenameTableLock; #endif /* Number localization, used by jsnum.c */ const char *thousandsSeparator; const char *decimalSeparator; const char *numGrouping; /* * Weak references to lazily-created, well-known XML singletons. * * NB: Singleton objects must be carefully disconnected from the rest of * the object graph usually associated with a JSContext's global object, * including the set of standard class objects. See jsxml.c for details. */ JSObject *anynameObject; JSObject *functionNamespaceObject; /* * A helper list for the GC, so it can mark native iterator states. See * js_MarkNativeIteratorStates for details. */ JSNativeIteratorState *nativeIteratorStates; #ifndef JS_THREADSAFE /* * For thread-unsafe embeddings, the GSN cache lives in the runtime and * not each context, since we expect it to be filled once when decompiling * a longer script, then hit repeatedly as js_GetSrcNote is called during * the decompiler activation that filled it. */ JSGSNCache gsnCache; #define JS_GSN_CACHE(cx) ((cx)->runtime->gsnCache) #endif #ifdef DEBUG /* Function invocation metering. */ jsrefcount inlineCalls; jsrefcount nativeCalls; jsrefcount nonInlineCalls; jsrefcount constructs; /* Scope lock and property metering. */ jsrefcount claimAttempts; jsrefcount claimedScopes; jsrefcount deadContexts; jsrefcount deadlocksAvoided; jsrefcount liveScopes; jsrefcount sharedScopes; jsrefcount totalScopes; jsrefcount badUndependStrings; jsrefcount liveScopeProps; jsrefcount totalScopeProps; jsrefcount livePropTreeNodes; jsrefcount duplicatePropTreeNodes; jsrefcount totalPropTreeNodes; jsrefcount propTreeKidsChunks; jsrefcount middleDeleteFixups; /* String instrumentation. */ jsrefcount liveStrings; jsrefcount totalStrings; jsrefcount liveDependentStrings; jsrefcount totalDependentStrings; double lengthSum; double lengthSquaredSum; double strdepLengthSum; double strdepLengthSquaredSum; #endif }; #ifdef DEBUG # define JS_RUNTIME_METER(rt, which) JS_ATOMIC_INCREMENT(&(rt)->which) # define JS_RUNTIME_UNMETER(rt, which) JS_ATOMIC_DECREMENT(&(rt)->which) #else # define JS_RUNTIME_METER(rt, which) /* nothing */ # define JS_RUNTIME_UNMETER(rt, which) /* nothing */ #endif #define JS_KEEP_ATOMS(rt) JS_ATOMIC_INCREMENT(&(rt)->gcKeepAtoms); #define JS_UNKEEP_ATOMS(rt) JS_ATOMIC_DECREMENT(&(rt)->gcKeepAtoms); #ifdef JS_ARGUMENT_FORMATTER_DEFINED /* * Linked list mapping format strings for JS_{Convert,Push}Arguments{,VA} to * formatter functions. Elements are sorted in non-increasing format string * length order. */ struct JSArgumentFormatMap { const char *format; size_t length; JSArgumentFormatter formatter; JSArgumentFormatMap *next; }; #endif struct JSStackHeader { uintN nslots; JSStackHeader *down; }; #define JS_STACK_SEGMENT(sh) ((jsval *)(sh) + 2) /* * Key and entry types for the JSContext.resolvingTable hash table, typedef'd * here because all consumers need to see these declarations (and not just the * typedef names, as would be the case for an opaque pointer-to-typedef'd-type * declaration), along with cx->resolvingTable. */ typedef struct JSResolvingKey { JSObject *obj; jsid id; } JSResolvingKey; typedef struct JSResolvingEntry { JSDHashEntryHdr hdr; JSResolvingKey key; uint32 flags; } JSResolvingEntry; #define JSRESFLAG_LOOKUP 0x1 /* resolving id from lookup */ #define JSRESFLAG_WATCH 0x2 /* resolving id from watch */ typedef struct JSLocalRootChunk JSLocalRootChunk; #define JSLRS_CHUNK_SHIFT 8 #define JSLRS_CHUNK_SIZE JS_BIT(JSLRS_CHUNK_SHIFT) #define JSLRS_CHUNK_MASK JS_BITMASK(JSLRS_CHUNK_SHIFT) struct JSLocalRootChunk { jsval roots[JSLRS_CHUNK_SIZE]; JSLocalRootChunk *down; }; typedef struct JSLocalRootStack { uint32 scopeMark; uint32 rootCount; JSLocalRootChunk *topChunk; JSLocalRootChunk firstChunk; } JSLocalRootStack; #define JSLRS_NULL_MARK ((uint32) -1) typedef struct JSTempValueRooter JSTempValueRooter; typedef void (* JS_DLL_CALLBACK JSTempValueMarker)(JSContext *cx, JSTempValueRooter *tvr); typedef union JSTempValueUnion { jsval value; JSObject *object; JSString *string; void *gcthing; JSTempValueMarker marker; JSScopeProperty *sprop; JSWeakRoots *weakRoots; jsval *array; } JSTempValueUnion; /* * The following allows to reinterpret JSTempValueUnion.object as jsval using * the tagging property of a generic jsval described below. */ JS_STATIC_ASSERT(sizeof(JSTempValueUnion) == sizeof(jsval)); JS_STATIC_ASSERT(sizeof(JSTempValueUnion) == sizeof(JSObject *)); /* * Context-linked stack of temporary GC roots. * * If count is -1, then u.value contains the single value or GC-thing to root. * If count is -2, then u.marker holds a mark hook called to mark the values. * If count is -3, then u.sprop points to the property tree node to mark. * If count is -4, then u.weakRoots points to saved weak roots. * If count >= 0, then u.array points to a stack-allocated vector of jsvals. * * To root a single GC-thing pointer, which need not be tagged and stored as a * jsval, use JS_PUSH_TEMP_ROOT_GCTHING. The macro reinterprets an arbitrary * GC-thing as jsval. It works because a GC-thing is aligned on a 0 mod 8 * boundary, and object has the 0 jsval tag. So any GC-thing may be tagged as * if it were an object and untagged, if it's then used only as an opaque * pointer until discriminated by other means than tag bits (this is how the * GC mark function uses its |thing| parameter -- it consults GC-thing flags * stored separately from the thing to decide the type of thing). * * JS_PUSH_TEMP_ROOT_OBJECT and JS_PUSH_TEMP_ROOT_STRING are type-safe * alternatives to JS_PUSH_TEMP_ROOT_GCTHING for JSObject and JSString. They * also provide a simple way to get a single pointer to rooted JSObject or * JSString via JS_PUSH_TEMP_ROOT_(OBJECT|STRTING)(cx, NULL, &tvr). Then * &tvr.u.object or tvr.u.string gives the necessary pointer, which puns * tvr.u.value safely because JSObject * and JSString * are GC-things and, as * such, their tag bits are all zeroes. * * If you need to protect a result value that flows out of a C function across * several layers of other functions, use the js_LeaveLocalRootScopeWithResult * internal API (see further below) instead. */ struct JSTempValueRooter { JSTempValueRooter *down; ptrdiff_t count; JSTempValueUnion u; }; #define JSTVU_SINGLE (-1) #define JSTVU_MARKER (-2) #define JSTVU_SPROP (-3) #define JSTVU_WEAK_ROOTS (-4) #define JS_PUSH_TEMP_ROOT_COMMON(cx,tvr) \ JS_BEGIN_MACRO \ JS_ASSERT((cx)->tempValueRooters != (tvr)); \ (tvr)->down = (cx)->tempValueRooters; \ (cx)->tempValueRooters = (tvr); \ JS_END_MACRO #define JS_PUSH_SINGLE_TEMP_ROOT(cx,val,tvr) \ JS_BEGIN_MACRO \ (tvr)->count = JSTVU_SINGLE; \ (tvr)->u.value = val; \ JS_PUSH_TEMP_ROOT_COMMON(cx, tvr); \ JS_END_MACRO #define JS_PUSH_TEMP_ROOT(cx,cnt,arr,tvr) \ JS_BEGIN_MACRO \ JS_ASSERT((ptrdiff_t)(cnt) >= 0); \ (tvr)->count = (ptrdiff_t)(cnt); \ (tvr)->u.array = (arr); \ JS_PUSH_TEMP_ROOT_COMMON(cx, tvr); \ JS_END_MACRO #define JS_PUSH_TEMP_ROOT_MARKER(cx,marker_,tvr) \ JS_BEGIN_MACRO \ (tvr)->count = JSTVU_MARKER; \ (tvr)->u.marker = (marker_); \ JS_PUSH_TEMP_ROOT_COMMON(cx, tvr); \ JS_END_MACRO #define JS_PUSH_TEMP_ROOT_OBJECT(cx,obj,tvr) \ JS_BEGIN_MACRO \ (tvr)->count = JSTVU_SINGLE; \ (tvr)->u.object = (obj); \ JS_PUSH_TEMP_ROOT_COMMON(cx, tvr); \ JS_END_MACRO #define JS_PUSH_TEMP_ROOT_STRING(cx,str,tvr) \ JS_BEGIN_MACRO \ (tvr)->count = JSTVU_SINGLE; \ (tvr)->u.string = (str); \ JS_PUSH_TEMP_ROOT_COMMON(cx, tvr); \ JS_END_MACRO #define JS_PUSH_TEMP_ROOT_GCTHING(cx,thing,tvr) \ JS_BEGIN_MACRO \ JS_ASSERT(JSVAL_IS_OBJECT((jsval)thing)); \ (tvr)->count = JSTVU_SINGLE; \ (tvr)->u.gcthing = (thing); \ JS_PUSH_TEMP_ROOT_COMMON(cx, tvr); \ JS_END_MACRO #define JS_POP_TEMP_ROOT(cx,tvr) \ JS_BEGIN_MACRO \ JS_ASSERT((cx)->tempValueRooters == (tvr)); \ (cx)->tempValueRooters = (tvr)->down; \ JS_END_MACRO #define JS_TEMP_ROOT_EVAL(cx,cnt,val,expr) \ JS_BEGIN_MACRO \ JSTempValueRooter tvr; \ JS_PUSH_TEMP_ROOT(cx, cnt, val, &tvr); \ (expr); \ JS_POP_TEMP_ROOT(cx, &tvr); \ JS_END_MACRO #define JS_PUSH_TEMP_ROOT_SPROP(cx,sprop_,tvr) \ JS_BEGIN_MACRO \ (tvr)->count = JSTVU_SPROP; \ (tvr)->u.sprop = (sprop_); \ JS_PUSH_TEMP_ROOT_COMMON(cx, tvr); \ JS_END_MACRO #define JS_PUSH_TEMP_ROOT_WEAK_COPY(cx,weakRoots_,tvr) \ JS_BEGIN_MACRO \ (tvr)->count = JSTVU_WEAK_ROOTS; \ (tvr)->u.weakRoots = (weakRoots_); \ JS_PUSH_TEMP_ROOT_COMMON(cx, tvr); \ JS_END_MACRO struct JSContext { /* JSRuntime contextList linkage. */ JSCList links; /* Interpreter activation count. */ uintN interpLevel; /* Limit pointer for checking stack consumption during recursion. */ jsuword stackLimit; /* Runtime version control identifier and equality operators. */ uint16 version; jsbytecode jsop_eq; jsbytecode jsop_ne; /* Data shared by threads in an address space. */ JSRuntime *runtime; /* Stack arena pool and frame pointer register. */ JSArenaPool stackPool; JSStackFrame *fp; /* Temporary arena pool used while compiling and decompiling. */ JSArenaPool tempPool; /* Top-level object and pointer to top stack frame's scope chain. */ JSObject *globalObject; /* Storage to root recently allocated GC things and script result. */ JSWeakRoots weakRoots; /* Regular expression class statics (XXX not shared globally). */ JSRegExpStatics regExpStatics; /* State for object and array toSource conversion. */ JSSharpObjectMap sharpObjectMap; /* Argument formatter support for JS_{Convert,Push}Arguments{,VA}. */ JSArgumentFormatMap *argumentFormatMap; /* Last message string and trace file for debugging. */ char *lastMessage; #ifdef DEBUG void *tracefp; #endif /* Per-context optional user callbacks. */ JSBranchCallback branchCallback; JSErrorReporter errorReporter; /* Client opaque pointer */ void *data; /* GC and thread-safe state. */ JSStackFrame *dormantFrameChain; /* dormant stack frame to scan */ #ifdef JS_THREADSAFE JSThread *thread; jsrefcount requestDepth; JSScope *scopeToShare; /* weak reference, see jslock.c */ JSScope *lockedSealedScope; /* weak ref, for low-cost sealed scope locking */ JSCList threadLinks; /* JSThread contextList linkage */ #define CX_FROM_THREAD_LINKS(tl) \ ((JSContext *)((char *)(tl) - offsetof(JSContext, threadLinks))) #endif #if JS_HAS_LVALUE_RETURN /* * Secondary return value from native method called on the left-hand side * of an assignment operator. The native should store the object in which * to set a property in *rval, and return the property's id expressed as a * jsval by calling JS_SetCallReturnValue2(cx, idval). */ jsval rval2; JSPackedBool rval2set; #endif #if JS_HAS_XML_SUPPORT /* * Bit-set formed from binary exponentials of the XML_* tiny-ids defined * for boolean settings in jsxml.c, plus an XSF_CACHE_VALID bit. Together * these act as a cache of the boolean XML.ignore* and XML.prettyPrinting * property values associated with this context's global object. */ uint8 xmlSettingFlags; #endif /* * True if creating an exception object, to prevent runaway recursion. * NB: creatingException packs with rval2set, #if JS_HAS_LVALUE_RETURN; * with xmlSettingFlags, #if JS_HAS_XML_SUPPORT; and with throwing below. */ JSPackedBool creatingException; /* * Exception state -- the exception member is a GC root by definition. * NB: throwing packs with creatingException and rval2set, above. */ JSPackedBool throwing; /* is there a pending exception? */ jsval exception; /* most-recently-thrown exception */ /* Flag to indicate that we run inside gcCallback(cx, JSGC_MARK_END). */ JSPackedBool insideGCMarkCallback; /* Per-context options. */ uint32 options; /* see jsapi.h for JSOPTION_* */ /* Locale specific callbacks for string conversion. */ JSLocaleCallbacks *localeCallbacks; /* * cx->resolvingTable is non-null and non-empty if we are initializing * standard classes lazily, or if we are otherwise recursing indirectly * from js_LookupProperty through a JSClass.resolve hook. It is used to * limit runaway recursion (see jsapi.c and jsobj.c). */ JSDHashTable *resolvingTable; /* PDL of stack headers describing stack slots not rooted by argv, etc. */ JSStackHeader *stackHeaders; /* Optional stack of heap-allocated scoped local GC roots. */ JSLocalRootStack *localRootStack; /* Stack of thread-stack-allocated temporary GC roots. */ JSTempValueRooter *tempValueRooters; #ifdef GC_MARK_DEBUG /* Top of the GC mark stack. */ void *gcCurrentMarkNode; #endif }; #ifdef JS_THREADSAFE # define JS_THREAD_ID(cx) ((cx)->thread ? (cx)->thread->id : 0) #endif #ifdef __cplusplus /* FIXME(bug 332648): Move this into a public header. */ class JSAutoTempValueRooter { public: JSAutoTempValueRooter(JSContext *cx, size_t len, jsval *vec) : mContext(cx) { JS_PUSH_TEMP_ROOT(mContext, len, vec, &mTvr); } JSAutoTempValueRooter(JSContext *cx, jsval v) : mContext(cx) { JS_PUSH_SINGLE_TEMP_ROOT(mContext, v, &mTvr); } ~JSAutoTempValueRooter() { JS_POP_TEMP_ROOT(mContext, &mTvr); } private: static void *operator new(size_t); static void operator delete(void *, size_t); JSContext *mContext; JSTempValueRooter mTvr; }; #endif /* * Slightly more readable macros for testing per-context option settings (also * to hide bitset implementation detail). * * JSOPTION_XML must be handled specially in order to propagate from compile- * to run-time (from cx->options to script->version/cx->version). To do that, * we copy JSOPTION_XML from cx->options into cx->version as JSVERSION_HAS_XML * whenever options are set, and preserve this XML flag across version number * changes done via the JS_SetVersion API. * * But when executing a script or scripted function, the interpreter changes * cx->version, including the XML flag, to script->version. Thus JSOPTION_XML * is a compile-time option that causes a run-time version change during each * activation of the compiled script. That version change has the effect of * changing JS_HAS_XML_OPTION, so that any compiling done via eval enables XML * support. If an XML-enabled script or function calls a non-XML function, * the flag bit will be cleared during the callee's activation. * * Note that JS_SetVersion API calls never pass JSVERSION_HAS_XML or'd into * that API's version parameter. * * Note also that script->version must contain this XML option flag in order * for XDR'ed scripts to serialize and deserialize with that option preserved * for detection at run-time. We can't copy other compile-time options into * script->version because that would break backward compatibility (certain * other options, e.g. JSOPTION_VAROBJFIX, are analogous to JSOPTION_XML). */ #define JS_HAS_OPTION(cx,option) (((cx)->options & (option)) != 0) #define JS_HAS_STRICT_OPTION(cx) JS_HAS_OPTION(cx, JSOPTION_STRICT) #define JS_HAS_WERROR_OPTION(cx) JS_HAS_OPTION(cx, JSOPTION_WERROR) #define JS_HAS_COMPILE_N_GO_OPTION(cx) JS_HAS_OPTION(cx, JSOPTION_COMPILE_N_GO) #define JS_HAS_ATLINE_OPTION(cx) JS_HAS_OPTION(cx, JSOPTION_ATLINE) #define JSVERSION_MASK 0x0FFF /* see JSVersion in jspubtd.h */ #define JSVERSION_HAS_XML 0x1000 /* flag induced by XML option */ #define JSVERSION_NUMBER(cx) ((cx)->version & JSVERSION_MASK) #define JS_HAS_XML_OPTION(cx) ((cx)->version & JSVERSION_HAS_XML || \ JSVERSION_NUMBER(cx) >= JSVERSION_1_6) #define JS_HAS_NATIVE_BRANCH_CALLBACK_OPTION(cx) \ JS_HAS_OPTION(cx, JSOPTION_NATIVE_BRANCH_CALLBACK) /* * Wrappers for the JSVERSION_IS_* macros from jspubtd.h taking JSContext *cx * and masking off the XML flag and any other high order bits. */ #define JS_VERSION_IS_ECMA(cx) JSVERSION_IS_ECMA(JSVERSION_NUMBER(cx)) /* * Common subroutine of JS_SetVersion and js_SetVersion, to update per-context * data that depends on version. */ extern void js_OnVersionChange(JSContext *cx); /* * Unlike the JS_SetVersion API, this function stores JSVERSION_HAS_XML and * any future non-version-number flags induced by compiler options. */ extern void js_SetVersion(JSContext *cx, JSVersion version); /* * Create and destroy functions for JSContext, which is manually allocated * and exclusively owned. */ extern JSContext * js_NewContext(JSRuntime *rt, size_t stackChunkSize); extern void js_DestroyContext(JSContext *cx, JSDestroyContextMode mode); /* * Return true if cx points to a context in rt->contextList, else return false. * NB: the caller (see jslock.c:ClaimScope) must hold rt->gcLock. */ extern JSBool js_ValidContextPointer(JSRuntime *rt, JSContext *cx); /* * If unlocked, acquire and release rt->gcLock around *iterp update; otherwise * the caller must be holding rt->gcLock. */ extern JSContext * js_ContextIterator(JSRuntime *rt, JSBool unlocked, JSContext **iterp); /* * JSClass.resolve and watchpoint recursion damping machinery. */ extern JSBool js_StartResolving(JSContext *cx, JSResolvingKey *key, uint32 flag, JSResolvingEntry **entryp); extern void js_StopResolving(JSContext *cx, JSResolvingKey *key, uint32 flag, JSResolvingEntry *entry, uint32 generation); /* * Local root set management. * * NB: the jsval parameters below may be properly tagged jsvals, or GC-thing * pointers cast to (jsval). This relies on JSObject's tag being zero, but * on the up side it lets us push int-jsval-encoded scopeMark values on the * local root stack. */ extern JSBool js_EnterLocalRootScope(JSContext *cx); #define js_LeaveLocalRootScope(cx) \ js_LeaveLocalRootScopeWithResult(cx, JSVAL_NULL) extern void js_LeaveLocalRootScopeWithResult(JSContext *cx, jsval rval); extern void js_ForgetLocalRoot(JSContext *cx, jsval v); extern int js_PushLocalRoot(JSContext *cx, JSLocalRootStack *lrs, jsval v); extern void js_MarkLocalRoots(JSContext *cx, JSLocalRootStack *lrs); /* * Report an exception, which is currently realized as a printf-style format * string and its arguments. */ typedef enum JSErrNum { #define MSG_DEF(name, number, count, exception, format) \ name = number, #include "js.msg" #undef MSG_DEF JSErr_Limit } JSErrNum; extern const JSErrorFormatString * js_GetErrorMessage(void *userRef, const char *locale, const uintN errorNumber); #ifdef va_start extern JSBool js_ReportErrorVA(JSContext *cx, uintN flags, const char *format, va_list ap); extern JSBool js_ReportErrorNumberVA(JSContext *cx, uintN flags, JSErrorCallback callback, void *userRef, const uintN errorNumber, JSBool charArgs, va_list ap); extern JSBool js_ExpandErrorArguments(JSContext *cx, JSErrorCallback callback, void *userRef, const uintN errorNumber, char **message, JSErrorReport *reportp, JSBool *warningp, JSBool charArgs, va_list ap); #endif extern void js_ReportOutOfMemory(JSContext *cx); /* * Report an exception using a previously composed JSErrorReport. * XXXbe remove from "friend" API */ extern JS_FRIEND_API(void) js_ReportErrorAgain(JSContext *cx, const char *message, JSErrorReport *report); extern void js_ReportIsNotDefined(JSContext *cx, const char *name); extern JSErrorFormatString js_ErrorFormatString[JSErr_Limit]; /* * See JS_SetThreadStackLimit in jsapi.c, where we check that the stack grows * in the expected direction. On Unix-y systems, JS_STACK_GROWTH_DIRECTION is * computed on the build host by jscpucfg.c and written into jsautocfg.h. The * macro is hardcoded in jscpucfg.h on Windows and Mac systems (for historical * reasons pre-dating autoconf usage). */ #if JS_STACK_GROWTH_DIRECTION > 0 # define JS_CHECK_STACK_SIZE(cx, lval) ((jsuword)&(lval) < (cx)->stackLimit) #else # define JS_CHECK_STACK_SIZE(cx, lval) ((jsuword)&(lval) > (cx)->stackLimit) #endif JS_END_EXTERN_C #endif /* jscntxt_h___ */ pacparser-1.4.5/src/spidermonkey/js/src/jscompat.h000066400000000000000000000044311464010763600222060ustar00rootroot00000000000000/* ***** BEGIN LICENSE BLOCK ***** * Version: MPL 1.1/GPL 2.0/LGPL 2.1 * * The contents of this file are subject to the Mozilla Public License Version * 1.1 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * http://www.mozilla.org/MPL/ * * Software distributed under the License is distributed on an "AS IS" basis, * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License * for the specific language governing rights and limitations under the * License. * * The Original Code is Mozilla Communicator client code, released * March 31, 1998. * * The Initial Developer of the Original Code is * Netscape Communications Corporation. * Portions created by the Initial Developer are Copyright (C) 1998-1999 * the Initial Developer. All Rights Reserved. * * Contributor(s): * * Alternatively, the contents of this file may be used under the terms of * either of the GNU General Public License Version 2 or later (the "GPL"), * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), * in which case the provisions of the GPL or the LGPL are applicable instead * of those above. If you wish to allow use of your version of this file only * under the terms of either the GPL or the LGPL, and not to allow others to * use your version of this file under the terms of the MPL, indicate your * decision by deleting the provisions above and replace them with the notice * and other provisions required by the GPL or the LGPL. If you do not delete * the provisions above, a recipient may use your version of this file under * the terms of any one of the MPL, the GPL or the LGPL. * * ***** END LICENSE BLOCK ***** */ /* -*- Mode: C; tab-width: 8 -*- * Copyright (C) 1996-1999 Netscape Communications Corporation, All Rights Reserved. */ #ifndef jscompat_h___ #define jscompat_h___ /* * Compatibility glue for various NSPR versions. We must always define int8, * int16, jsword, and so on to minimize differences with js/ref, no matter what * the NSPR typedef names may be. */ #include "jstypes.h" #include "jslong.h" typedef JSIntn intN; typedef JSUintn uintN; typedef JSUword jsuword; typedef JSWord jsword; typedef float float32; #define allocPriv allocPool #endif /* jscompat_h___ */ pacparser-1.4.5/src/spidermonkey/js/src/jsconfig.h000066400000000000000000000265331464010763600221770ustar00rootroot00000000000000/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- * * ***** BEGIN LICENSE BLOCK ***** * Version: MPL 1.1/GPL 2.0/LGPL 2.1 * * The contents of this file are subject to the Mozilla Public License Version * 1.1 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * http://www.mozilla.org/MPL/ * * Software distributed under the License is distributed on an "AS IS" basis, * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License * for the specific language governing rights and limitations under the * License. * * The Original Code is Mozilla Communicator client code, released * March 31, 1998. * * The Initial Developer of the Original Code is * Netscape Communications Corporation. * Portions created by the Initial Developer are Copyright (C) 1998 * the Initial Developer. All Rights Reserved. * * Contributor(s): * * Alternatively, the contents of this file may be used under the terms of * either of the GNU General Public License Version 2 or later (the "GPL"), * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), * in which case the provisions of the GPL or the LGPL are applicable instead * of those above. If you wish to allow use of your version of this file only * under the terms of either the GPL or the LGPL, and not to allow others to * use your version of this file under the terms of the MPL, indicate your * decision by deleting the provisions above and replace them with the notice * and other provisions required by the GPL or the LGPL. If you do not delete * the provisions above, a recipient may use your version of this file under * the terms of any one of the MPL, the GPL or the LGPL. * * ***** END LICENSE BLOCK ***** */ /* * JS configuration macros. */ #ifndef JS_VERSION #define JS_VERSION 170 #endif /* * Compile-time JS version configuration. The JS version numbers lie on the * number line like so: * * 1.0 1.1 1.2 1.3 1.4 ECMAv3 1.5 1.6 * ^ ^ * | | * basis for ECMAv1 close to ECMAv2 * * where ECMAv3 stands for ECMA-262 Edition 3. See the runtime version enum * JSVersion in jspubtd.h. Code in the engine can therefore count on version * <= JSVERSION_1_4 to mean "before the Third Edition of ECMA-262" and version * > JSVERSION_1_4 to mean "at or after the Third Edition". * * In the (likely?) event that SpiderMonkey grows to implement JavaScript 2.0, * or ECMA-262 Edition 4 (JS2 without certain extensions), the version number * to use would be near 200, or greater. * * The JS_VERSION_ECMA_3 version is the minimal configuration conforming to * the ECMA-262 Edition 3 specification. Use it for minimal embeddings, where * you're sure you don't need any of the extensions disabled in this version. * In order to facilitate testing, JS_HAS_OBJ_PROTO_PROP is defined as part of * the JS_VERSION_ECMA_3_TEST version. * * To keep things sane in the modern age, where we need exceptions in order to * implement, e.g., iterators and generators, we are dropping support for all * versions <= 1.4. */ #define JS_VERSION_ECMA_3 148 #define JS_VERSION_ECMA_3_TEST 149 #if JS_VERSION == JS_VERSION_ECMA_3 || \ JS_VERSION == JS_VERSION_ECMA_3_TEST #define JS_HAS_STR_HTML_HELPERS 0 /* has str.anchor, str.bold, etc. */ #define JS_HAS_PERL_SUBSTR 0 /* has str.substr */ #if JS_VERSION == JS_VERSION_ECMA_3_TEST #define JS_HAS_OBJ_PROTO_PROP 1 /* has o.__proto__ etc. */ #else #define JS_HAS_OBJ_PROTO_PROP 0 /* has o.__proto__ etc. */ #endif #define JS_HAS_OBJ_WATCHPOINT 0 /* has o.watch and o.unwatch */ #define JS_HAS_EXPORT_IMPORT 0 /* has export fun; import obj.fun */ #define JS_HAS_EVAL_THIS_SCOPE 0 /* Math.eval is same as with (Math) */ #define JS_HAS_SHARP_VARS 0 /* has #n=, #n# for object literals */ #define JS_HAS_SCRIPT_OBJECT 0 /* has (new Script("x++")).exec() */ #define JS_HAS_XDR 0 /* has XDR API and internal support */ #define JS_HAS_XDR_FREEZE_THAW 0 /* has XDR freeze/thaw script methods */ #define JS_HAS_TOSOURCE 0 /* has Object/Array toSource method */ #define JS_HAS_DEBUGGER_KEYWORD 0 /* has hook for debugger keyword */ #define JS_HAS_CATCH_GUARD 0 /* has exception handling catch guard */ #define JS_HAS_SPARSE_ARRAYS 0 /* array methods preserve empty elems */ #define JS_HAS_GETTER_SETTER 0 /* has JS2 getter/setter functions */ #define JS_HAS_UNEVAL 0 /* has uneval() top-level function */ #define JS_HAS_CONST 0 /* has JS2 const as alternative var */ #define JS_HAS_FUN_EXPR_STMT 0 /* has function expression statement */ #define JS_HAS_LVALUE_RETURN 1 /* has o.item(i) = j; for native item */ #define JS_HAS_NO_SUCH_METHOD 0 /* has o.__noSuchMethod__ handler */ #define JS_HAS_XML_SUPPORT 0 /* has ECMAScript for XML support */ #define JS_HAS_ARRAY_EXTRAS 0 /* has indexOf and Lispy extras */ #define JS_HAS_GENERATORS 0 /* has yield in generator function */ #define JS_HAS_BLOCK_SCOPE 0 /* has block scope via let/arraycomp */ #define JS_HAS_DESTRUCTURING 0 /* has [a,b] = ... or {p:a,q:b} = ... */ #elif JS_VERSION < 150 #error "unsupported JS_VERSION" #elif JS_VERSION == 150 #define JS_HAS_STR_HTML_HELPERS 1 /* has str.anchor, str.bold, etc. */ #define JS_HAS_PERL_SUBSTR 1 /* has str.substr */ #define JS_HAS_OBJ_PROTO_PROP 1 /* has o.__proto__ etc. */ #define JS_HAS_OBJ_WATCHPOINT 1 /* has o.watch and o.unwatch */ #define JS_HAS_EXPORT_IMPORT 1 /* has export fun; import obj.fun */ #define JS_HAS_EVAL_THIS_SCOPE 1 /* Math.eval is same as with (Math) */ #define JS_HAS_SHARP_VARS 1 /* has #n=, #n# for object literals */ #define JS_HAS_SCRIPT_OBJECT 1 /* has (new Script("x++")).exec() */ #define JS_HAS_XDR 1 /* has XDR API and internal support */ #define JS_HAS_XDR_FREEZE_THAW 0 /* has XDR freeze/thaw script methods */ #define JS_HAS_TOSOURCE 1 /* has Object/Array toSource method */ #define JS_HAS_DEBUGGER_KEYWORD 1 /* has hook for debugger keyword */ #define JS_HAS_CATCH_GUARD 1 /* has exception handling catch guard */ #define JS_HAS_SPARSE_ARRAYS 0 /* array methods preserve empty elems */ #define JS_HAS_GETTER_SETTER 1 /* has JS2 getter/setter functions */ #define JS_HAS_UNEVAL 1 /* has uneval() top-level function */ #define JS_HAS_CONST 1 /* has JS2 const as alternative var */ #define JS_HAS_FUN_EXPR_STMT 1 /* has function expression statement */ #define JS_HAS_LVALUE_RETURN 1 /* has o.item(i) = j; for native item */ #define JS_HAS_NO_SUCH_METHOD 1 /* has o.__noSuchMethod__ handler */ #define JS_HAS_XML_SUPPORT 0 /* has ECMAScript for XML support */ #define JS_HAS_ARRAY_EXTRAS 0 /* has indexOf and Lispy extras */ #define JS_HAS_GENERATORS 0 /* has yield in generator function */ #define JS_HAS_BLOCK_SCOPE 0 /* has block scope via let/arraycomp */ #define JS_HAS_DESTRUCTURING 0 /* has [a,b] = ... or {p:a,q:b} = ... */ #elif JS_VERSION == 160 #define JS_HAS_STR_HTML_HELPERS 1 /* has str.anchor, str.bold, etc. */ #define JS_HAS_PERL_SUBSTR 1 /* has str.substr */ #define JS_HAS_OBJ_PROTO_PROP 1 /* has o.__proto__ etc. */ #define JS_HAS_OBJ_WATCHPOINT 1 /* has o.watch and o.unwatch */ #define JS_HAS_EXPORT_IMPORT 1 /* has export fun; import obj.fun */ #define JS_HAS_EVAL_THIS_SCOPE 1 /* Math.eval is same as with (Math) */ #define JS_HAS_SHARP_VARS 1 /* has #n=, #n# for object literals */ #define JS_HAS_SCRIPT_OBJECT 1 /* has (new Script("x++")).exec() */ #define JS_HAS_XDR 1 /* has XDR API and internal support */ #define JS_HAS_XDR_FREEZE_THAW 0 /* has XDR freeze/thaw script methods */ #define JS_HAS_TOSOURCE 1 /* has Object/Array toSource method */ #define JS_HAS_DEBUGGER_KEYWORD 1 /* has hook for debugger keyword */ #define JS_HAS_CATCH_GUARD 1 /* has exception handling catch guard */ #define JS_HAS_SPARSE_ARRAYS 0 /* array methods preserve empty elems */ #define JS_HAS_GETTER_SETTER 1 /* has JS2 getter/setter functions */ #define JS_HAS_UNEVAL 1 /* has uneval() top-level function */ #define JS_HAS_CONST 1 /* has JS2 const as alternative var */ #define JS_HAS_FUN_EXPR_STMT 1 /* has function expression statement */ #define JS_HAS_LVALUE_RETURN 1 /* has o.item(i) = j; for native item */ #define JS_HAS_NO_SUCH_METHOD 1 /* has o.__noSuchMethod__ handler */ #define JS_HAS_XML_SUPPORT 1 /* has ECMAScript for XML support */ #define JS_HAS_ARRAY_EXTRAS 1 /* has indexOf and Lispy extras */ #define JS_HAS_GENERATORS 0 /* has yield in generator function */ #define JS_HAS_BLOCK_SCOPE 0 /* has block scope via let/arraycomp */ #define JS_HAS_DESTRUCTURING 0 /* has [a,b] = ... or {p:a,q:b} = ... */ #elif JS_VERSION == 170 #define JS_HAS_STR_HTML_HELPERS 1 /* has str.anchor, str.bold, etc. */ #define JS_HAS_PERL_SUBSTR 1 /* has str.substr */ #define JS_HAS_OBJ_PROTO_PROP 1 /* has o.__proto__ etc. */ #define JS_HAS_OBJ_WATCHPOINT 1 /* has o.watch and o.unwatch */ #define JS_HAS_EXPORT_IMPORT 1 /* has export fun; import obj.fun */ #define JS_HAS_EVAL_THIS_SCOPE 1 /* Math.eval is same as with (Math) */ #define JS_HAS_SHARP_VARS 1 /* has #n=, #n# for object literals */ #define JS_HAS_SCRIPT_OBJECT 1 /* has (new Script("x++")).exec() */ #define JS_HAS_XDR 1 /* has XDR API and internal support */ #define JS_HAS_XDR_FREEZE_THAW 0 /* has XDR freeze/thaw script methods */ #define JS_HAS_TOSOURCE 1 /* has Object/Array toSource method */ #define JS_HAS_DEBUGGER_KEYWORD 1 /* has hook for debugger keyword */ #define JS_HAS_CATCH_GUARD 1 /* has exception handling catch guard */ #define JS_HAS_SPARSE_ARRAYS 0 /* array methods preserve empty elems */ #define JS_HAS_GETTER_SETTER 1 /* has JS2 getter/setter functions */ #define JS_HAS_UNEVAL 1 /* has uneval() top-level function */ #define JS_HAS_CONST 1 /* has JS2 const as alternative var */ #define JS_HAS_FUN_EXPR_STMT 1 /* has function expression statement */ #define JS_HAS_LVALUE_RETURN 1 /* has o.item(i) = j; for native item */ #define JS_HAS_NO_SUCH_METHOD 1 /* has o.__noSuchMethod__ handler */ #define JS_HAS_XML_SUPPORT 1 /* has ECMAScript for XML support */ #define JS_HAS_ARRAY_EXTRAS 1 /* has indexOf and Lispy extras */ #define JS_HAS_GENERATORS 1 /* has yield in generator function */ #define JS_HAS_BLOCK_SCOPE 1 /* has block scope via let/arraycomp */ #define JS_HAS_DESTRUCTURING 1 /* has [a,b] = ... or {p:a,q:b} = ... */ #else #error "unknown JS_VERSION" #endif /* Features that are present in all versions. */ #define JS_HAS_RESERVED_JAVA_KEYWORDS 1 #define JS_HAS_RESERVED_ECMA_KEYWORDS 1 pacparser-1.4.5/src/spidermonkey/js/src/jsconfig.mk000066400000000000000000000127761464010763600223630ustar00rootroot00000000000000# -*- Mode: makefile -*- # # ***** BEGIN LICENSE BLOCK ***** # Version: MPL 1.1/GPL 2.0/LGPL 2.1 # # The contents of this file are subject to the Mozilla Public License Version # 1.1 (the "License"); you may not use this file except in compliance with # the License. You may obtain a copy of the License at # http://www.mozilla.org/MPL/ # # Software distributed under the License is distributed on an "AS IS" basis, # WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License # for the specific language governing rights and limitations under the # License. # # The Original Code is Mozilla Communicator client code, released # March 31, 1998. # # The Initial Developer of the Original Code is # Netscape Communications Corporation. # Portions created by the Initial Developer are Copyright (C) 1998-1999 # the Initial Developer. All Rights Reserved. # # Contributor(s): # # Alternatively, the contents of this file may be used under the terms of # either of the GNU General Public License Version 2 or later (the "GPL"), # or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), # in which case the provisions of the GPL or the LGPL are applicable instead # of those above. If you wish to allow use of your version of this file only # under the terms of either the GPL or the LGPL, and not to allow others to # use your version of this file under the terms of the MPL, indicate your # decision by deleting the provisions above and replace them with the notice # and other provisions required by the GPL or the LGPL. If you do not delete # the provisions above, a recipient may use your version of this file under # the terms of any one of the MPL, the GPL or the LGPL. # # ***** END LICENSE BLOCK ***** ifndef OBJDIR ifdef OBJDIR_NAME OBJDIR = $(OBJDIR_NAME) endif endif NSPR_VERSION = v4.0 NSPR_LIBSUFFIX = 4 NSPR_LOCAL = $(MOZ_DEPTH)/dist/$(OBJDIR)/nspr NSPR_DIST = $(MOZ_DEPTH)/dist/$(OBJDIR) NSPR_OBJDIR = $(OBJDIR) ifeq ($(OS_ARCH), SunOS) NSPR_OBJDIR := $(subst _sparc,,$(NSPR_OBJDIR)) endif ifeq ($(OS_ARCH), Linux) LINUX_REL := $(shell uname -r) ifneq (,$(findstring 2.0,$(LINUX_REL))) NSPR_OBJDIR := $(subst _All,2.0_x86_glibc_PTH,$(NSPR_OBJDIR)) else NSPR_OBJDIR := $(subst _All,2.2_x86_glibc_PTH,$(NSPR_OBJDIR)) endif endif ifeq ($(OS_ARCH), AIX) NSPR_OBJDIR := $(subst 4.1,4.2,$(NSPR_OBJDIR)) endif ifeq ($(OS_CONFIG), IRIX6.2) NSPR_OBJDIR := $(subst 6.2,6.2_n32_PTH,$(NSPR_OBJDIR)) endif ifeq ($(OS_CONFIG), IRIX6.5) NSPR_OBJDIR := $(subst 6.5,6.5_n32_PTH,$(NSPR_OBJDIR)) endif ifeq ($(OS_ARCH), WINNT) ifeq ($(OBJDIR), WIN32_D.OBJ) NSPR_OBJDIR = WINNT4.0_DBG.OBJ endif ifeq ($(OBJDIR), WIN32_O.OBJ) NSPR_OBJDIR = WINNT4.0_OPT.OBJ endif endif NSPR_SHARED = /share/builds/components/nspr20/$(NSPR_VERSION)/$(NSPR_OBJDIR) ifeq ($(OS_ARCH), WINNT) NSPR_SHARED = nspr20/$(NSPR_VERSION)/$(NSPR_OBJDIR) endif NSPR_VERSIONFILE = $(NSPR_LOCAL)/Version NSPR_CURVERSION := $(shell cat $(NSPR_VERSIONFILE)) get_nspr: @echo "Grabbing NSPR component..." ifeq ($(NSPR_VERSION), $(NSPR_CURVERSION)) @echo "No need, NSPR is up to date in this tree (ver=$(NSPR_VERSION))." else mkdir -p $(NSPR_LOCAL) mkdir -p $(NSPR_DIST) ifneq ($(OS_ARCH), WINNT) cp $(NSPR_SHARED)/*.jar $(NSPR_LOCAL) else sh $(MOZ_DEPTH)/../reltools/compftp.sh $(NSPR_SHARED) $(NSPR_LOCAL) *.jar endif unzip -o $(NSPR_LOCAL)/mdbinary.jar -d $(NSPR_DIST) mkdir -p $(NSPR_DIST)/include unzip -o $(NSPR_LOCAL)/mdheader.jar -d $(NSPR_DIST)/include rm -rf $(NSPR_DIST)/META-INF rm -rf $(NSPR_DIST)/include/META-INF echo $(NSPR_VERSION) > $(NSPR_VERSIONFILE) endif SHIP_DIST = $(MOZ_DEPTH)/dist/$(OBJDIR) SHIP_DIR = $(SHIP_DIST)/SHIP SHIP_LIBS = libjs.$(SO_SUFFIX) libjs.a ifdef JS_LIVECONNECT SHIP_LIBS += libjsj.$(SO_SUFFIX) libjsj.a endif ifeq ($(OS_ARCH), WINNT) SHIP_LIBS = js32.dll js32.lib ifdef JS_LIVECONNECT SHIP_LIBS += jsj.dll jsj.lib endif endif SHIP_LIBS += $(LCJAR) SHIP_LIBS := $(addprefix $(SHIP_DIST)/lib/, $(SHIP_LIBS)) SHIP_INCS = js*.h prmjtime.h resource.h *.msg *.tbl ifdef JS_LIVECONNECT SHIP_INCS += netscape*.h nsC*.h nsI*.h endif SHIP_INCS := $(addprefix $(SHIP_DIST)/include/, $(SHIP_INCS)) SHIP_BINS = js ifdef JS_LIVECONNECT SHIP_BINS += lcshell endif ifeq ($(OS_ARCH), WINNT) SHIP_BINS := $(addsuffix .exe, $(SHIP_BINS)) endif SHIP_BINS := $(addprefix $(SHIP_DIST)/bin/, $(SHIP_BINS)) ifdef BUILD_OPT JSREFJAR = jsref_opt.jar else ifdef BUILD_IDG JSREFJAR = jsref_idg.jar else JSREFJAR = jsref_dbg.jar endif endif ship: mkdir -p $(SHIP_DIR)/$(LIBDIR) mkdir -p $(SHIP_DIR)/include mkdir -p $(SHIP_DIR)/bin cp $(SHIP_LIBS) $(SHIP_DIR)/$(LIBDIR) cp $(SHIP_INCS) $(SHIP_DIR)/include cp $(SHIP_BINS) $(SHIP_DIR)/bin cd $(SHIP_DIR); \ zip -r $(JSREFJAR) bin lib include ifdef BUILD_SHIP cp $(SHIP_DIR)/$(JSREFJAR) $(BUILD_SHIP) endif CWD = $(shell pwd) shipSource: $(SHIP_DIR)/jsref_src.lst .FORCE mkdir -p $(SHIP_DIR) cd $(MOZ_DEPTH)/.. ; \ zip $(CWD)/$(SHIP_DIR)/jsref_src.jar -@ < $(CWD)/$(SHIP_DIR)/jsref_src.lst ifdef BUILD_SHIP cp $(SHIP_DIR)/jsref_src.jar $(BUILD_SHIP) endif JSREFSRCDIRS := $(shell cat $(DEPTH)/SpiderMonkey.rsp) $(SHIP_DIR)/jsref_src.lst: .FORCE mkdir -p $(SHIP_DIR) rm -f $@ touch $@ for d in $(JSREFSRCDIRS); do \ cd $(MOZ_DEPTH)/..; \ ls -1 -d $$d | grep -v CVS | grep -v \.OBJ >> $(CWD)/$@; \ cd $(CWD); \ done .FORCE: pacparser-1.4.5/src/spidermonkey/js/src/jscpucfg.c000066400000000000000000000301271464010763600221660ustar00rootroot00000000000000/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- * * ***** BEGIN LICENSE BLOCK ***** * Version: MPL 1.1/GPL 2.0/LGPL 2.1 * * The contents of this file are subject to the Mozilla Public License Version * 1.1 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * http://www.mozilla.org/MPL/ * * Software distributed under the License is distributed on an "AS IS" basis, * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License * for the specific language governing rights and limitations under the * License. * * The Original Code is Mozilla Communicator client code, released * March 31, 1998. * * The Initial Developer of the Original Code is * Netscape Communications Corporation. * Portions created by the Initial Developer are Copyright (C) 1998 * the Initial Developer. All Rights Reserved. * * Contributor(s): * Roland Mainz * * Alternatively, the contents of this file may be used under the terms of * either of the GNU General Public License Version 2 or later (the "GPL"), * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), * in which case the provisions of the GPL or the LGPL are applicable instead * of those above. If you wish to allow use of your version of this file only * under the terms of either the GPL or the LGPL, and not to allow others to * use your version of this file under the terms of the MPL, indicate your * decision by deleting the provisions above and replace them with the notice * and other provisions required by the GPL or the LGPL. If you do not delete * the provisions above, a recipient may use your version of this file under * the terms of any one of the MPL, the GPL or the LGPL. * * ***** END LICENSE BLOCK ***** */ /* * Generate CPU-specific bit-size and similar #defines. */ #include #include #ifdef CROSS_COMPILE #include #define INT64 PRInt64 #else /************************************************************************/ /* Generate cpucfg.h */ #if defined(XP_WIN) || defined(XP_OS2) #ifdef WIN32 #if defined(__GNUC__) #define INT64 long long #else #define INT64 _int64 #endif /* __GNUC__ */ #else #define INT64 long #endif #else #if defined(HPUX) || defined(__QNX__) || defined(_SCO_DS) || defined(UNIXWARE) #define INT64 long #else #define INT64 long long #endif #endif #endif /* CROSS_COMPILE */ #ifdef __GNUC__ #define NS_NEVER_INLINE __attribute__((noinline)) #else #define NS_NEVER_INLINE #endif #ifdef __SUNPRO_C static int StackGrowthDirection(int *dummy1addr); #pragma no_inline(StackGrowthDirection) #endif typedef void *prword; struct align_short { char c; short a; }; struct align_int { char c; int a; }; struct align_long { char c; long a; }; struct align_int64 { char c; INT64 a; }; struct align_fakelonglong { char c; struct { long hi, lo; } a; }; struct align_float { char c; float a; }; struct align_double { char c; double a; }; struct align_pointer { char c; void *a; }; struct align_prword { char c; prword a; }; #define ALIGN_OF(type) \ (((char*)&(((struct align_##type *)0)->a)) - ((char*)0)) unsigned int bpb; static int Log2(unsigned int n) { int log2 = 0; if (n & (n-1)) log2++; if (n >> 16) log2 += 16, n >>= 16; if (n >> 8) log2 += 8, n >>= 8; if (n >> 4) log2 += 4, n >>= 4; if (n >> 2) log2 += 2, n >>= 2; if (n >> 1) log2++; return log2; } /* * Conceivably this could actually be used, but there is lots of code out * there with ands and shifts in it that assumes a byte is exactly 8 bits, * so forget about porting THIS code to all those non 8 bit byte machines. */ static void BitsPerByte(void) { bpb = 8; } static int NS_NEVER_INLINE StackGrowthDirection(int *dummy1addr) { int dummy2; return (&dummy2 < dummy1addr) ? -1 : 1; } int main(int argc, char **argv) { int sizeof_char, sizeof_short, sizeof_int, sizeof_int64, sizeof_long, sizeof_float, sizeof_double, sizeof_word, sizeof_dword; int bits_per_int64_log2, align_of_short, align_of_int, align_of_long, align_of_int64, align_of_float, align_of_double, align_of_pointer, align_of_word; int dummy1; BitsPerByte(); printf("#ifndef js_cpucfg___\n"); printf("#define js_cpucfg___\n\n"); printf("/* AUTOMATICALLY GENERATED - DO NOT EDIT */\n\n"); #ifdef CROSS_COMPILE #if defined(IS_LITTLE_ENDIAN) printf("#define IS_LITTLE_ENDIAN 1\n"); printf("#undef IS_BIG_ENDIAN\n\n"); #elif defined(IS_BIG_ENDIAN) printf("#undef IS_LITTLE_ENDIAN\n"); printf("#define IS_BIG_ENDIAN 1\n\n"); #else #error "Endianess not defined." #endif sizeof_char = PR_BYTES_PER_BYTE; sizeof_short = PR_BYTES_PER_SHORT; sizeof_int = PR_BYTES_PER_INT; sizeof_int64 = PR_BYTES_PER_INT64; sizeof_long = PR_BYTES_PER_LONG; sizeof_float = PR_BYTES_PER_FLOAT; sizeof_double = PR_BYTES_PER_DOUBLE; sizeof_word = PR_BYTES_PER_WORD; sizeof_dword = PR_BYTES_PER_DWORD; bits_per_int64_log2 = PR_BITS_PER_INT64_LOG2; align_of_short = PR_ALIGN_OF_SHORT; align_of_int = PR_ALIGN_OF_INT; align_of_long = PR_ALIGN_OF_LONG; align_of_int64 = PR_ALIGN_OF_INT64; align_of_float = PR_ALIGN_OF_FLOAT; align_of_double = PR_ALIGN_OF_DOUBLE; align_of_pointer = PR_ALIGN_OF_POINTER; align_of_word = PR_ALIGN_OF_WORD; #else /* !CROSS_COMPILE */ /* * We don't handle PDP-endian or similar orders: if a short is big-endian, * so must int and long be big-endian for us to generate the IS_BIG_ENDIAN * #define and the IS_LITTLE_ENDIAN #undef. */ { int big_endian = 0, little_endian = 0, ntests = 0; if (sizeof(short) == 2) { /* force |volatile| here to get rid of any compiler optimisations * (var in register etc.) which may be appiled to |auto| vars - * even those in |union|s... * (|static| is used to get the same functionality for compilers * which do not honor |volatile|...). */ volatile static union { short i; char c[2]; } u; u.i = 0x0102; big_endian += (u.c[0] == 0x01 && u.c[1] == 0x02); little_endian += (u.c[0] == 0x02 && u.c[1] == 0x01); ntests++; } if (sizeof(int) == 4) { /* force |volatile| here ... */ volatile static union { int i; char c[4]; } u; u.i = 0x01020304; big_endian += (u.c[0] == 0x01 && u.c[1] == 0x02 && u.c[2] == 0x03 && u.c[3] == 0x04); little_endian += (u.c[0] == 0x04 && u.c[1] == 0x03 && u.c[2] == 0x02 && u.c[3] == 0x01); ntests++; } if (sizeof(long) == 8) { /* force |volatile| here ... */ volatile static union { long i; char c[8]; } u; /* * Write this as portably as possible: avoid 0x0102030405060708L * and <<= 32. */ u.i = 0x01020304; u.i <<= 16, u.i <<= 16; u.i |= 0x05060708; big_endian += (u.c[0] == 0x01 && u.c[1] == 0x02 && u.c[2] == 0x03 && u.c[3] == 0x04 && u.c[4] == 0x05 && u.c[5] == 0x06 && u.c[6] == 0x07 && u.c[7] == 0x08); little_endian += (u.c[0] == 0x08 && u.c[1] == 0x07 && u.c[2] == 0x06 && u.c[3] == 0x05 && u.c[4] == 0x04 && u.c[5] == 0x03 && u.c[6] == 0x02 && u.c[7] == 0x01); ntests++; } if (big_endian && big_endian == ntests) { printf("#undef IS_LITTLE_ENDIAN\n"); printf("#define IS_BIG_ENDIAN 1\n\n"); } else if (little_endian && little_endian == ntests) { printf("#define IS_LITTLE_ENDIAN 1\n"); printf("#undef IS_BIG_ENDIAN\n\n"); } else { fprintf(stderr, "%s: unknown byte order" "(big_endian=%d, little_endian=%d, ntests=%d)!\n", argv[0], big_endian, little_endian, ntests); return EXIT_FAILURE; } } sizeof_char = sizeof(char); sizeof_short = sizeof(short); sizeof_int = sizeof(int); sizeof_int64 = 8; sizeof_long = sizeof(long); sizeof_float = sizeof(float); sizeof_double = sizeof(double); sizeof_word = sizeof(prword); sizeof_dword = 8; bits_per_int64_log2 = 6; align_of_short = ALIGN_OF(short); align_of_int = ALIGN_OF(int); align_of_long = ALIGN_OF(long); if (sizeof(INT64) < 8) { /* this machine doesn't actually support int64's */ align_of_int64 = ALIGN_OF(fakelonglong); } else { align_of_int64 = ALIGN_OF(int64); } align_of_float = ALIGN_OF(float); align_of_double = ALIGN_OF(double); align_of_pointer = ALIGN_OF(pointer); align_of_word = ALIGN_OF(prword); #endif /* CROSS_COMPILE */ printf("#define JS_BYTES_PER_BYTE %dL\n", sizeof_char); printf("#define JS_BYTES_PER_SHORT %dL\n", sizeof_short); printf("#define JS_BYTES_PER_INT %dL\n", sizeof_int); printf("#define JS_BYTES_PER_INT64 %dL\n", sizeof_int64); printf("#define JS_BYTES_PER_LONG %dL\n", sizeof_long); printf("#define JS_BYTES_PER_FLOAT %dL\n", sizeof_float); printf("#define JS_BYTES_PER_DOUBLE %dL\n", sizeof_double); printf("#define JS_BYTES_PER_WORD %dL\n", sizeof_word); printf("#define JS_BYTES_PER_DWORD %dL\n", sizeof_dword); printf("\n"); printf("#define JS_BITS_PER_BYTE %dL\n", bpb); printf("#define JS_BITS_PER_SHORT %dL\n", bpb * sizeof_short); printf("#define JS_BITS_PER_INT %dL\n", bpb * sizeof_int); printf("#define JS_BITS_PER_INT64 %dL\n", bpb * sizeof_int64); printf("#define JS_BITS_PER_LONG %dL\n", bpb * sizeof_long); printf("#define JS_BITS_PER_FLOAT %dL\n", bpb * sizeof_float); printf("#define JS_BITS_PER_DOUBLE %dL\n", bpb * sizeof_double); printf("#define JS_BITS_PER_WORD %dL\n", bpb * sizeof_word); printf("\n"); printf("#define JS_BITS_PER_BYTE_LOG2 %dL\n", Log2(bpb)); printf("#define JS_BITS_PER_SHORT_LOG2 %dL\n", Log2(bpb * sizeof_short)); printf("#define JS_BITS_PER_INT_LOG2 %dL\n", Log2(bpb * sizeof_int)); printf("#define JS_BITS_PER_INT64_LOG2 %dL\n", bits_per_int64_log2); printf("#define JS_BITS_PER_LONG_LOG2 %dL\n", Log2(bpb * sizeof_long)); printf("#define JS_BITS_PER_FLOAT_LOG2 %dL\n", Log2(bpb * sizeof_float)); printf("#define JS_BITS_PER_DOUBLE_LOG2 %dL\n", Log2(bpb * sizeof_double)); printf("#define JS_BITS_PER_WORD_LOG2 %dL\n", Log2(bpb * sizeof_word)); printf("\n"); printf("#define JS_ALIGN_OF_SHORT %dL\n", align_of_short); printf("#define JS_ALIGN_OF_INT %dL\n", align_of_int); printf("#define JS_ALIGN_OF_LONG %dL\n", align_of_long); printf("#define JS_ALIGN_OF_INT64 %dL\n", align_of_int64); printf("#define JS_ALIGN_OF_FLOAT %dL\n", align_of_float); printf("#define JS_ALIGN_OF_DOUBLE %dL\n", align_of_double); printf("#define JS_ALIGN_OF_POINTER %dL\n", align_of_pointer); printf("#define JS_ALIGN_OF_WORD %dL\n", align_of_word); printf("\n"); printf("#define JS_BYTES_PER_WORD_LOG2 %dL\n", Log2(sizeof_word)); printf("#define JS_BYTES_PER_DWORD_LOG2 %dL\n", Log2(sizeof_dword)); printf("#define JS_WORDS_PER_DWORD_LOG2 %dL\n", Log2(sizeof_dword/sizeof_word)); printf("\n"); printf("#define JS_STACK_GROWTH_DIRECTION (%d)\n", StackGrowthDirection(&dummy1)); printf("\n"); printf("#endif /* js_cpucfg___ */\n"); return EXIT_SUCCESS; } pacparser-1.4.5/src/spidermonkey/js/src/jscpucfg.h000066400000000000000000000147111464010763600221740ustar00rootroot00000000000000/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- * * ***** BEGIN LICENSE BLOCK ***** * Version: MPL 1.1/GPL 2.0/LGPL 2.1 * * The contents of this file are subject to the Mozilla Public License Version * 1.1 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * http://www.mozilla.org/MPL/ * * Software distributed under the License is distributed on an "AS IS" basis, * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License * for the specific language governing rights and limitations under the * License. * * The Original Code is Mozilla Communicator client code, released * March 31, 1998. * * The Initial Developer of the Original Code is * Netscape Communications Corporation. * Portions created by the Initial Developer are Copyright (C) 1998 * the Initial Developer. All Rights Reserved. * * Contributor(s): * * Alternatively, the contents of this file may be used under the terms of * either of the GNU General Public License Version 2 or later (the "GPL"), * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), * in which case the provisions of the GPL or the LGPL are applicable instead * of those above. If you wish to allow use of your version of this file only * under the terms of either the GPL or the LGPL, and not to allow others to * use your version of this file under the terms of the MPL, indicate your * decision by deleting the provisions above and replace them with the notice * and other provisions required by the GPL or the LGPL. If you do not delete * the provisions above, a recipient may use your version of this file under * the terms of any one of the MPL, the GPL or the LGPL. * * ***** END LICENSE BLOCK ***** */ #ifndef js_cpucfg___ #define js_cpucfg___ #include "jsosdep.h" #if defined(XP_WIN) || defined(XP_OS2) || defined(WINCE) #if defined(_WIN64) #if defined(_M_X64) || defined(_M_AMD64) || defined(_AMD64_) #define IS_LITTLE_ENDIAN 1 #undef IS_BIG_ENDIAN #define JS_BYTES_PER_BYTE 1L #define JS_BYTES_PER_SHORT 2L #define JS_BYTES_PER_INT 4L #define JS_BYTES_PER_INT64 8L #define JS_BYTES_PER_LONG 4L #define JS_BYTES_PER_FLOAT 4L #define JS_BYTES_PER_DOUBLE 8L #define JS_BYTES_PER_WORD 8L #define JS_BYTES_PER_DWORD 8L #define JS_BITS_PER_BYTE 8L #define JS_BITS_PER_SHORT 16L #define JS_BITS_PER_INT 32L #define JS_BITS_PER_INT64 64L #define JS_BITS_PER_LONG 32L #define JS_BITS_PER_FLOAT 32L #define JS_BITS_PER_DOUBLE 64L #define JS_BITS_PER_WORD 64L #define JS_BITS_PER_BYTE_LOG2 3L #define JS_BITS_PER_SHORT_LOG2 4L #define JS_BITS_PER_INT_LOG2 5L #define JS_BITS_PER_INT64_LOG2 6L #define JS_BITS_PER_LONG_LOG2 5L #define JS_BITS_PER_FLOAT_LOG2 5L #define JS_BITS_PER_DOUBLE_LOG2 6L #define JS_BITS_PER_WORD_LOG2 6L #define JS_ALIGN_OF_SHORT 2L #define JS_ALIGN_OF_INT 4L #define JS_ALIGN_OF_LONG 4L #define JS_ALIGN_OF_INT64 8L #define JS_ALIGN_OF_FLOAT 4L #define JS_ALIGN_OF_DOUBLE 8L #define JS_ALIGN_OF_POINTER 8L #define JS_ALIGN_OF_WORD 8L #define JS_BYTES_PER_WORD_LOG2 3L #define JS_BYTES_PER_DWORD_LOG2 3L #define PR_WORDS_PER_DWORD_LOG2 0L #else /* !(defined(_M_X64) || defined(_M_AMD64) || defined(_AMD64_)) */ #error "CPU type is unknown" #endif /* !(defined(_M_X64) || defined(_M_AMD64) || defined(_AMD64_)) */ #elif defined(_WIN32) || defined(XP_OS2) || defined(WINCE) #ifdef __WATCOMC__ #define HAVE_VA_LIST_AS_ARRAY #endif #define IS_LITTLE_ENDIAN 1 #undef IS_BIG_ENDIAN #define JS_BYTES_PER_BYTE 1L #define JS_BYTES_PER_SHORT 2L #define JS_BYTES_PER_INT 4L #define JS_BYTES_PER_INT64 8L #define JS_BYTES_PER_LONG 4L #define JS_BYTES_PER_FLOAT 4L #define JS_BYTES_PER_DOUBLE 8L #define JS_BYTES_PER_WORD 4L #define JS_BYTES_PER_DWORD 8L #define JS_BITS_PER_BYTE 8L #define JS_BITS_PER_SHORT 16L #define JS_BITS_PER_INT 32L #define JS_BITS_PER_INT64 64L #define JS_BITS_PER_LONG 32L #define JS_BITS_PER_FLOAT 32L #define JS_BITS_PER_DOUBLE 64L #define JS_BITS_PER_WORD 32L #define JS_BITS_PER_BYTE_LOG2 3L #define JS_BITS_PER_SHORT_LOG2 4L #define JS_BITS_PER_INT_LOG2 5L #define JS_BITS_PER_INT64_LOG2 6L #define JS_BITS_PER_LONG_LOG2 5L #define JS_BITS_PER_FLOAT_LOG2 5L #define JS_BITS_PER_DOUBLE_LOG2 6L #define JS_BITS_PER_WORD_LOG2 5L #define JS_ALIGN_OF_SHORT 2L #define JS_ALIGN_OF_INT 4L #define JS_ALIGN_OF_LONG 4L #define JS_ALIGN_OF_INT64 8L #define JS_ALIGN_OF_FLOAT 4L #define JS_ALIGN_OF_DOUBLE 4L #define JS_ALIGN_OF_POINTER 4L #define JS_ALIGN_OF_WORD 4L #define JS_BYTES_PER_WORD_LOG2 2L #define JS_BYTES_PER_DWORD_LOG2 3L #define PR_WORDS_PER_DWORD_LOG2 1L #endif /* _WIN32 || XP_OS2 || WINCE*/ #if defined(_WINDOWS) && !defined(_WIN32) /* WIN16 */ #define IS_LITTLE_ENDIAN 1 #undef IS_BIG_ENDIAN #define JS_BYTES_PER_BYTE 1L #define JS_BYTES_PER_SHORT 2L #define JS_BYTES_PER_INT 2L #define JS_BYTES_PER_INT64 8L #define JS_BYTES_PER_LONG 4L #define JS_BYTES_PER_FLOAT 4L #define JS_BYTES_PER_DOUBLE 8L #define JS_BYTES_PER_WORD 4L #define JS_BYTES_PER_DWORD 8L #define JS_BITS_PER_BYTE 8L #define JS_BITS_PER_SHORT 16L #define JS_BITS_PER_INT 16L #define JS_BITS_PER_INT64 64L #define JS_BITS_PER_LONG 32L #define JS_BITS_PER_FLOAT 32L #define JS_BITS_PER_DOUBLE 64L #define JS_BITS_PER_WORD 32L #define JS_BITS_PER_BYTE_LOG2 3L #define JS_BITS_PER_SHORT_LOG2 4L #define JS_BITS_PER_INT_LOG2 4L #define JS_BITS_PER_INT64_LOG2 6L #define JS_BITS_PER_LONG_LOG2 5L #define JS_BITS_PER_FLOAT_LOG2 5L #define JS_BITS_PER_DOUBLE_LOG2 6L #define JS_BITS_PER_WORD_LOG2 5L #define JS_ALIGN_OF_SHORT 2L #define JS_ALIGN_OF_INT 2L #define JS_ALIGN_OF_LONG 2L #define JS_ALIGN_OF_INT64 2L #define JS_ALIGN_OF_FLOAT 2L #define JS_ALIGN_OF_DOUBLE 2L #define JS_ALIGN_OF_POINTER 2L #define JS_ALIGN_OF_WORD 2L #define JS_BYTES_PER_WORD_LOG2 2L #define JS_BYTES_PER_DWORD_LOG2 3L #define PR_WORDS_PER_DWORD_LOG2 1L #endif /* defined(_WINDOWS) && !defined(_WIN32) */ #elif defined(XP_UNIX) || defined(XP_BEOS) #error "This file is supposed to be auto-generated on UNIX platforms, but the" #error "static version for Mac and Windows platforms is being used." #error "Something's probably wrong with paths/headers/dependencies/Makefiles." #else #error "Must define one of XP_BEOS, XP_OS2, XP_WIN, or XP_UNIX" #endif #ifndef JS_STACK_GROWTH_DIRECTION #define JS_STACK_GROWTH_DIRECTION (-1) #endif #endif /* js_cpucfg___ */ pacparser-1.4.5/src/spidermonkey/js/src/jsdate.c000066400000000000000000002100151464010763600216300ustar00rootroot00000000000000/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- * * ***** BEGIN LICENSE BLOCK ***** * Version: MPL 1.1/GPL 2.0/LGPL 2.1 * * The contents of this file are subject to the Mozilla Public License Version * 1.1 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * http://www.mozilla.org/MPL/ * * Software distributed under the License is distributed on an "AS IS" basis, * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License * for the specific language governing rights and limitations under the * License. * * The Original Code is Mozilla Communicator client code, released * March 31, 1998. * * The Initial Developer of the Original Code is * Netscape Communications Corporation. * Portions created by the Initial Developer are Copyright (C) 1998 * the Initial Developer. All Rights Reserved. * * Contributor(s): * * Alternatively, the contents of this file may be used under the terms of * either of the GNU General Public License Version 2 or later (the "GPL"), * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), * in which case the provisions of the GPL or the LGPL are applicable instead * of those above. If you wish to allow use of your version of this file only * under the terms of either the GPL or the LGPL, and not to allow others to * use your version of this file under the terms of the MPL, indicate your * decision by deleting the provisions above and replace them with the notice * and other provisions required by the GPL or the LGPL. If you do not delete * the provisions above, a recipient may use your version of this file under * the terms of any one of the MPL, the GPL or the LGPL. * * ***** END LICENSE BLOCK ***** */ /* * JS date methods. */ /* * "For example, OS/360 devotes 26 bytes of the permanently * resident date-turnover routine to the proper handling of * December 31 on leap years (when it is Day 366). That * might have been left to the operator." * * Frederick Brooks, 'The Second-System Effect'. */ #include "jsstddef.h" #include #include #include #include #include #include "jstypes.h" #include "jsprf.h" #include "prmjtime.h" #include "jsutil.h" /* Added by JSIFY */ #include "jsapi.h" #include "jsconfig.h" #include "jscntxt.h" #include "jsdate.h" #include "jsinterp.h" #include "jsnum.h" #include "jsobj.h" #include "jsstr.h" /* * The JS 'Date' object is patterned after the Java 'Date' object. * Here is an script: * * today = new Date(); * * print(today.toLocaleString()); * * weekDay = today.getDay(); * * * These Java (and ECMA-262) methods are supported: * * UTC * getDate (getUTCDate) * getDay (getUTCDay) * getHours (getUTCHours) * getMinutes (getUTCMinutes) * getMonth (getUTCMonth) * getSeconds (getUTCSeconds) * getMilliseconds (getUTCMilliseconds) * getTime * getTimezoneOffset * getYear * getFullYear (getUTCFullYear) * parse * setDate (setUTCDate) * setHours (setUTCHours) * setMinutes (setUTCMinutes) * setMonth (setUTCMonth) * setSeconds (setUTCSeconds) * setMilliseconds (setUTCMilliseconds) * setTime * setYear (setFullYear, setUTCFullYear) * toGMTString (toUTCString) * toLocaleString * toString * * * These Java methods are not supported * * setDay * before * after * equals * hashCode */ /* * 11/97 - jsdate.c has been rewritten to conform to the ECMA-262 language * definition and reduce dependence on NSPR. NSPR is used to get the current * time in milliseconds, the time zone offset, and the daylight savings time * offset for a given time. NSPR is also used for Date.toLocaleString(), for * locale-specific formatting, and to get a string representing the timezone. * (Which turns out to be platform-dependent.) * * To do: * (I did some performance tests by timing how long it took to run what * I had of the js ECMA conformance tests.) * * - look at saving results across multiple calls to supporting * functions; the toString functions compute some of the same values * multiple times. Although - I took a quick stab at this, and I lost * rather than gained. (Fractionally.) Hard to tell what compilers/processors * are doing these days. * * - look at tweaking function return types to return double instead * of int; this seems to make things run slightly faster sometimes. * (though it could be architecture-dependent.) It'd be good to see * how this does on win32. (Tried it on irix.) Types could use a * general going-over. */ /* * Supporting functions - ECMA 15.9.1.* */ #define HalfTimeDomain 8.64e15 #define HoursPerDay 24.0 #define MinutesPerDay (HoursPerDay * MinutesPerHour) #define MinutesPerHour 60.0 #define SecondsPerDay (MinutesPerDay * SecondsPerMinute) #define SecondsPerHour (MinutesPerHour * SecondsPerMinute) #define SecondsPerMinute 60.0 #if defined(XP_WIN) || defined(XP_OS2) /* Work around msvc double optimization bug by making these runtime values; if * they're available at compile time, msvc optimizes division by them by * computing the reciprocal and multiplying instead of dividing - this loses * when the reciprocal isn't representable in a double. */ static jsdouble msPerSecond = 1000.0; static jsdouble msPerDay = SecondsPerDay * 1000.0; static jsdouble msPerHour = SecondsPerHour * 1000.0; static jsdouble msPerMinute = SecondsPerMinute * 1000.0; #else #define msPerDay (SecondsPerDay * msPerSecond) #define msPerHour (SecondsPerHour * msPerSecond) #define msPerMinute (SecondsPerMinute * msPerSecond) #define msPerSecond 1000.0 #endif #define Day(t) floor((t) / msPerDay) static jsdouble TimeWithinDay(jsdouble t) { jsdouble result; result = fmod(t, msPerDay); if (result < 0) result += msPerDay; return result; } #define DaysInYear(y) ((y) % 4 == 0 && ((y) % 100 || ((y) % 400 == 0)) \ ? 366 : 365) /* math here has to be f.p, because we need * floor((1968 - 1969) / 4) == -1 */ #define DayFromYear(y) (365 * ((y)-1970) + floor(((y)-1969)/4.0) \ - floor(((y)-1901)/100.0) + floor(((y)-1601)/400.0)) #define TimeFromYear(y) (DayFromYear(y) * msPerDay) static jsint YearFromTime(jsdouble t) { jsint y = (jsint) floor(t /(msPerDay*365.2425)) + 1970; jsdouble t2 = (jsdouble) TimeFromYear(y); if (t2 > t) { y--; } else { if (t2 + msPerDay * DaysInYear(y) <= t) y++; } return y; } #define InLeapYear(t) (JSBool) (DaysInYear(YearFromTime(t)) == 366) #define DayWithinYear(t, year) ((intN) (Day(t) - DayFromYear(year))) /* * The following array contains the day of year for the first day of * each month, where index 0 is January, and day 0 is January 1. */ static jsdouble firstDayOfMonth[2][12] = { {0.0, 31.0, 59.0, 90.0, 120.0, 151.0, 181.0, 212.0, 243.0, 273.0, 304.0, 334.0}, {0.0, 31.0, 60.0, 91.0, 121.0, 152.0, 182.0, 213.0, 244.0, 274.0, 305.0, 335.0} }; #define DayFromMonth(m, leap) firstDayOfMonth[leap][(intN)m]; static intN MonthFromTime(jsdouble t) { intN d, step; jsint year = YearFromTime(t); d = DayWithinYear(t, year); if (d < (step = 31)) return 0; step += (InLeapYear(t) ? 29 : 28); if (d < step) return 1; if (d < (step += 31)) return 2; if (d < (step += 30)) return 3; if (d < (step += 31)) return 4; if (d < (step += 30)) return 5; if (d < (step += 31)) return 6; if (d < (step += 31)) return 7; if (d < (step += 30)) return 8; if (d < (step += 31)) return 9; if (d < (step += 30)) return 10; return 11; } static intN DateFromTime(jsdouble t) { intN d, step, next; jsint year = YearFromTime(t); d = DayWithinYear(t, year); if (d <= (next = 30)) return d + 1; step = next; next += (InLeapYear(t) ? 29 : 28); if (d <= next) return d - step; step = next; if (d <= (next += 31)) return d - step; step = next; if (d <= (next += 30)) return d - step; step = next; if (d <= (next += 31)) return d - step; step = next; if (d <= (next += 30)) return d - step; step = next; if (d <= (next += 31)) return d - step; step = next; if (d <= (next += 31)) return d - step; step = next; if (d <= (next += 30)) return d - step; step = next; if (d <= (next += 31)) return d - step; step = next; if (d <= (next += 30)) return d - step; step = next; return d - step; } static intN WeekDay(jsdouble t) { jsint result; result = (jsint) Day(t) + 4; result = result % 7; if (result < 0) result += 7; return (intN) result; } #define MakeTime(hour, min, sec, ms) \ ((((hour) * MinutesPerHour + (min)) * SecondsPerMinute + (sec)) * msPerSecond + (ms)) static jsdouble MakeDay(jsdouble year, jsdouble month, jsdouble date) { JSBool leap; jsdouble yearday; jsdouble monthday; year += floor(month / 12); month = fmod(month, 12.0); if (month < 0) month += 12; leap = (DaysInYear((jsint) year) == 366); yearday = floor(TimeFromYear(year) / msPerDay); monthday = DayFromMonth(month, leap); return yearday + monthday + date - 1; } #define MakeDate(day, time) ((day) * msPerDay + (time)) /* * Years and leap years on which Jan 1 is a Sunday, Monday, etc. * * yearStartingWith[0][i] is an example non-leap year where * Jan 1 appears on Sunday (i == 0), Monday (i == 1), etc. * * yearStartingWith[1][i] is an example leap year where * Jan 1 appears on Sunday (i == 0), Monday (i == 1), etc. */ static jsint yearStartingWith[2][7] = { {1978, 1973, 1974, 1975, 1981, 1971, 1977}, {1984, 1996, 1980, 1992, 1976, 1988, 1972} }; /* * Find a year for which any given date will fall on the same weekday. * * This function should be used with caution when used other than * for determining DST; it hasn't been proven not to produce an * incorrect year for times near year boundaries. */ static jsint EquivalentYearForDST(jsint year) { jsint day; JSBool isLeapYear; day = (jsint) DayFromYear(year) + 4; day = day % 7; if (day < 0) day += 7; isLeapYear = (DaysInYear(year) == 366); return yearStartingWith[isLeapYear][day]; } /* LocalTZA gets set by js_InitDateClass() */ static jsdouble LocalTZA; static jsdouble DaylightSavingTA(jsdouble t) { volatile int64 PR_t; int64 ms2us; int64 offset; jsdouble result; /* abort if NaN */ if (JSDOUBLE_IS_NaN(t)) return t; /* * If earlier than 1970 or after 2038, potentially beyond the ken of * many OSes, map it to an equivalent year before asking. */ if (t < 0.0 || t > 2145916800000.0) { jsint year; jsdouble day; year = EquivalentYearForDST(YearFromTime(t)); day = MakeDay(year, MonthFromTime(t), DateFromTime(t)); t = MakeDate(day, TimeWithinDay(t)); } /* put our t in an LL, and map it to usec for prtime */ JSLL_D2L(PR_t, t); JSLL_I2L(ms2us, PRMJ_USEC_PER_MSEC); JSLL_MUL(PR_t, PR_t, ms2us); offset = PRMJ_DSTOffset(PR_t); JSLL_DIV(offset, offset, ms2us); JSLL_L2D(result, offset); return result; } #define AdjustTime(t) fmod(LocalTZA + DaylightSavingTA(t), msPerDay) #define LocalTime(t) ((t) + AdjustTime(t)) static jsdouble UTC(jsdouble t) { return t - AdjustTime(t - LocalTZA); } static intN HourFromTime(jsdouble t) { intN result = (intN) fmod(floor(t/msPerHour), HoursPerDay); if (result < 0) result += (intN)HoursPerDay; return result; } static intN MinFromTime(jsdouble t) { intN result = (intN) fmod(floor(t / msPerMinute), MinutesPerHour); if (result < 0) result += (intN)MinutesPerHour; return result; } static intN SecFromTime(jsdouble t) { intN result = (intN) fmod(floor(t / msPerSecond), SecondsPerMinute); if (result < 0) result += (intN)SecondsPerMinute; return result; } static intN msFromTime(jsdouble t) { intN result = (intN) fmod(t, msPerSecond); if (result < 0) result += (intN)msPerSecond; return result; } #define TIMECLIP(d) ((JSDOUBLE_IS_FINITE(d) \ && !((d < 0 ? -d : d) > HalfTimeDomain)) \ ? js_DoubleToInteger(d + (+0.)) : *cx->runtime->jsNaN) /** * end of ECMA 'support' functions */ /* * Other Support routines and definitions */ JSClass js_DateClass = { js_Date_str, JSCLASS_HAS_PRIVATE | JSCLASS_HAS_CACHED_PROTO(JSProto_Date), JS_PropertyStub, JS_PropertyStub, JS_PropertyStub, JS_PropertyStub, JS_EnumerateStub, JS_ResolveStub, JS_ConvertStub, JS_FinalizeStub, JSCLASS_NO_OPTIONAL_MEMBERS }; /* for use by date_parse */ static const char* wtb[] = { "am", "pm", "monday", "tuesday", "wednesday", "thursday", "friday", "saturday", "sunday", "january", "february", "march", "april", "may", "june", "july", "august", "september", "october", "november", "december", "gmt", "ut", "utc", "est", "edt", "cst", "cdt", "mst", "mdt", "pst", "pdt" /* time zone table needs to be expanded */ }; static int ttb[] = { -1, -2, 0, 0, 0, 0, 0, 0, 0, /* AM/PM */ 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 10000 + 0, 10000 + 0, 10000 + 0, /* GMT/UT/UTC */ 10000 + 5 * 60, 10000 + 4 * 60, /* EST/EDT */ 10000 + 6 * 60, 10000 + 5 * 60, /* CST/CDT */ 10000 + 7 * 60, 10000 + 6 * 60, /* MST/MDT */ 10000 + 8 * 60, 10000 + 7 * 60 /* PST/PDT */ }; /* helper for date_parse */ static JSBool date_regionMatches(const char* s1, int s1off, const jschar* s2, int s2off, int count, int ignoreCase) { JSBool result = JS_FALSE; /* return true if matches, otherwise, false */ while (count > 0 && s1[s1off] && s2[s2off]) { if (ignoreCase) { if (JS_TOLOWER((jschar)s1[s1off]) != JS_TOLOWER(s2[s2off])) { break; } } else { if ((jschar)s1[s1off] != s2[s2off]) { break; } } s1off++; s2off++; count--; } if (count == 0) { result = JS_TRUE; } return result; } /* find UTC time from given date... no 1900 correction! */ static jsdouble date_msecFromDate(jsdouble year, jsdouble mon, jsdouble mday, jsdouble hour, jsdouble min, jsdouble sec, jsdouble msec) { jsdouble day; jsdouble msec_time; jsdouble result; day = MakeDay(year, mon, mday); msec_time = MakeTime(hour, min, sec, msec); result = MakeDate(day, msec_time); return result; } /* * See ECMA 15.9.4.[3-10]; */ /* XXX this function must be above date_parseString to avoid a horrid bug in the Win16 1.52 compiler */ #define MAXARGS 7 static JSBool date_UTC(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) { jsdouble array[MAXARGS]; uintN loop; jsdouble d; for (loop = 0; loop < MAXARGS; loop++) { if (loop < argc) { if (!js_ValueToNumber(cx, argv[loop], &d)) return JS_FALSE; /* return NaN if any arg is NaN */ if (!JSDOUBLE_IS_FINITE(d)) { return js_NewNumberValue(cx, d, rval); } array[loop] = floor(d); } else { array[loop] = 0; } } /* adjust 2-digit years into the 20th century */ if (array[0] >= 0 && array[0] <= 99) array[0] += 1900; /* if we got a 0 for 'date' (which is out of range) * pretend it's a 1. (So Date.UTC(1972, 5) works) */ if (array[2] < 1) array[2] = 1; d = date_msecFromDate(array[0], array[1], array[2], array[3], array[4], array[5], array[6]); d = TIMECLIP(d); return js_NewNumberValue(cx, d, rval); } static JSBool date_parseString(JSString *str, jsdouble *result) { jsdouble msec; const jschar *s = JSSTRING_CHARS(str); size_t limit = JSSTRING_LENGTH(str); size_t i = 0; int year = -1; int mon = -1; int mday = -1; int hour = -1; int min = -1; int sec = -1; int c = -1; int n = -1; jsdouble tzoffset = -1; /* was an int, overflowed on win16!!! */ int prevc = 0; JSBool seenplusminus = JS_FALSE; int temp; JSBool seenmonthname = JS_FALSE; if (limit == 0) goto syntax; while (i < limit) { c = s[i]; i++; if (c <= ' ' || c == ',' || c == '-') { if (c == '-' && '0' <= s[i] && s[i] <= '9') { prevc = c; } continue; } if (c == '(') { /* comments) */ int depth = 1; while (i < limit) { c = s[i]; i++; if (c == '(') depth++; else if (c == ')') if (--depth <= 0) break; } continue; } if ('0' <= c && c <= '9') { n = c - '0'; while (i < limit && '0' <= (c = s[i]) && c <= '9') { n = n * 10 + c - '0'; i++; } /* allow TZA before the year, so * 'Wed Nov 05 21:49:11 GMT-0800 1997' * works */ /* uses of seenplusminus allow : in TZA, so Java * no-timezone style of GMT+4:30 works */ if ((prevc == '+' || prevc == '-')/* && year>=0 */) { /* make ':' case below change tzoffset */ seenplusminus = JS_TRUE; /* offset */ if (n < 24) n = n * 60; /* EG. "GMT-3" */ else n = n % 100 + n / 100 * 60; /* eg "GMT-0430" */ if (prevc == '+') /* plus means east of GMT */ n = -n; if (tzoffset != 0 && tzoffset != -1) goto syntax; tzoffset = n; } else if (prevc == '/' && mon >= 0 && mday >= 0 && year < 0) { if (c <= ' ' || c == ',' || c == '/' || i >= limit) year = n; else goto syntax; } else if (c == ':') { if (hour < 0) hour = /*byte*/ n; else if (min < 0) min = /*byte*/ n; else goto syntax; } else if (c == '/') { /* until it is determined that mon is the actual month, keep it as 1-based rather than 0-based */ if (mon < 0) mon = /*byte*/ n; else if (mday < 0) mday = /*byte*/ n; else goto syntax; } else if (i < limit && c != ',' && c > ' ' && c != '-' && c != '(') { goto syntax; } else if (seenplusminus && n < 60) { /* handle GMT-3:30 */ if (tzoffset < 0) tzoffset -= n; else tzoffset += n; } else if (hour >= 0 && min < 0) { min = /*byte*/ n; } else if (prevc == ':' && min >= 0 && sec < 0) { sec = /*byte*/ n; } else if (mon < 0) { mon = /*byte*/n; } else if (mon >= 0 && mday < 0) { mday = /*byte*/ n; } else if (mon >= 0 && mday >= 0 && year < 0) { year = n; } else { goto syntax; } prevc = 0; } else if (c == '/' || c == ':' || c == '+' || c == '-') { prevc = c; } else { size_t st = i - 1; int k; while (i < limit) { c = s[i]; if (!(('A' <= c && c <= 'Z') || ('a' <= c && c <= 'z'))) break; i++; } if (i <= st + 1) goto syntax; for (k = (sizeof(wtb)/sizeof(char*)); --k >= 0;) if (date_regionMatches(wtb[k], 0, s, st, i-st, 1)) { int action = ttb[k]; if (action != 0) { if (action < 0) { /* * AM/PM. Count 12:30 AM as 00:30, 12:30 PM as * 12:30, instead of blindly adding 12 if PM. */ JS_ASSERT(action == -1 || action == -2); if (hour > 12 || hour < 0) { goto syntax; } else { if (action == -1 && hour == 12) { /* am */ hour = 0; } else if (action == -2 && hour != 12) { /* pm */ hour += 12; } } } else if (action <= 13) { /* month! */ /* Adjust mon to be 1-based until the final values for mon, mday and year are adjusted below */ if (seenmonthname) { goto syntax; } seenmonthname = JS_TRUE; temp = /*byte*/ (action - 2) + 1; if (mon < 0) { mon = temp; } else if (mday < 0) { mday = mon; mon = temp; } else if (year < 0) { year = mon; mon = temp; } else { goto syntax; } } else { tzoffset = action - 10000; } } break; } if (k < 0) goto syntax; prevc = 0; } } if (year < 0 || mon < 0 || mday < 0) goto syntax; /* Case 1. The input string contains an English month name. The form of the string can be month f l, or f month l, or f l month which each evaluate to the same date. If f and l are both greater than or equal to 70, or both less than 70, the date is invalid. The year is taken to be the greater of the values f, l. If the year is greater than or equal to 70 and less than 100, it is considered to be the number of years after 1900. Case 2. The input string is of the form "f/m/l" where f, m and l are integers, e.g. 7/16/45. Adjust the mon, mday and year values to achieve 100% MSIE compatibility. a. If 0 <= f < 70, f/m/l is interpreted as month/day/year. i. If year < 100, it is the number of years after 1900 ii. If year >= 100, it is the number of years after 0. b. If 70 <= f < 100 i. If m < 70, f/m/l is interpreted as year/month/day where year is the number of years after 1900. ii. If m >= 70, the date is invalid. c. If f >= 100 i. If m < 70, f/m/l is interpreted as year/month/day where year is the number of years after 0. ii. If m >= 70, the date is invalid. */ if (seenmonthname) { if ((mday >= 70 && year >= 70) || (mday < 70 && year < 70)) { goto syntax; } if (mday > year) { temp = year; year = mday; mday = temp; } if (year >= 70 && year < 100) { year += 1900; } } else if (mon < 70) { /* (a) month/day/year */ if (year < 100) { year += 1900; } } else if (mon < 100) { /* (b) year/month/day */ if (mday < 70) { temp = year; year = mon + 1900; mon = mday; mday = temp; } else { goto syntax; } } else { /* (c) year/month/day */ if (mday < 70) { temp = year; year = mon; mon = mday; mday = temp; } else { goto syntax; } } mon -= 1; /* convert month to 0-based */ if (sec < 0) sec = 0; if (min < 0) min = 0; if (hour < 0) hour = 0; if (tzoffset == -1) { /* no time zone specified, have to use local */ jsdouble msec_time; msec_time = date_msecFromDate(year, mon, mday, hour, min, sec, 0); *result = UTC(msec_time); return JS_TRUE; } msec = date_msecFromDate(year, mon, mday, hour, min, sec, 0); msec += tzoffset * msPerMinute; *result = msec; return JS_TRUE; syntax: /* syntax error */ *result = 0; return JS_FALSE; } static JSBool date_parse(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) { JSString *str; jsdouble result; str = js_ValueToString(cx, argv[0]); if (!str) return JS_FALSE; if (!date_parseString(str, &result)) { *rval = DOUBLE_TO_JSVAL(cx->runtime->jsNaN); return JS_TRUE; } result = TIMECLIP(result); return js_NewNumberValue(cx, result, rval); } static JSBool date_now(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) { int64 us, ms, us2ms; jsdouble msec_time; us = PRMJ_Now(); JSLL_UI2L(us2ms, PRMJ_USEC_PER_MSEC); JSLL_DIV(ms, us, us2ms); JSLL_L2D(msec_time, ms); return js_NewDoubleValue(cx, msec_time, rval); } /* * Check that obj is an object of class Date, and get the date value. * Return NULL on failure. */ static jsdouble * date_getProlog(JSContext *cx, JSObject *obj, jsval *argv) { if (!JS_InstanceOf(cx, obj, &js_DateClass, argv)) return NULL; return JSVAL_TO_DOUBLE(OBJ_GET_SLOT(cx, obj, JSSLOT_PRIVATE)); } /* * See ECMA 15.9.5.4 thru 15.9.5.23 */ static JSBool date_getTime(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) { jsdouble *date = date_getProlog(cx, obj, argv); if (!date) return JS_FALSE; return js_NewNumberValue(cx, *date, rval); } static JSBool date_getYear(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) { jsdouble *date; jsdouble result; date = date_getProlog(cx, obj, argv); if (!date) return JS_FALSE; result = *date; if (!JSDOUBLE_IS_FINITE(result)) return js_NewNumberValue(cx, result, rval); result = YearFromTime(LocalTime(result)); /* Follow ECMA-262 to the letter, contrary to IE JScript. */ result -= 1900; return js_NewNumberValue(cx, result, rval); } static JSBool date_getFullYear(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) { jsdouble result; jsdouble *date = date_getProlog(cx, obj, argv); if (!date) return JS_FALSE; result = *date; if (!JSDOUBLE_IS_FINITE(result)) return js_NewNumberValue(cx, result, rval); result = YearFromTime(LocalTime(result)); return js_NewNumberValue(cx, result, rval); } static JSBool date_getUTCFullYear(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) { jsdouble result; jsdouble *date = date_getProlog(cx, obj, argv); if (!date) return JS_FALSE; result = *date; if (!JSDOUBLE_IS_FINITE(result)) return js_NewNumberValue(cx, result, rval); result = YearFromTime(result); return js_NewNumberValue(cx, result, rval); } static JSBool date_getMonth(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) { jsdouble result; jsdouble *date = date_getProlog(cx, obj, argv); if (!date) return JS_FALSE; result = *date; if (!JSDOUBLE_IS_FINITE(result)) return js_NewNumberValue(cx, result, rval); result = MonthFromTime(LocalTime(result)); return js_NewNumberValue(cx, result, rval); } static JSBool date_getUTCMonth(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) { jsdouble result; jsdouble *date = date_getProlog(cx, obj, argv); if (!date) return JS_FALSE; result = *date; if (!JSDOUBLE_IS_FINITE(result)) return js_NewNumberValue(cx, result, rval); result = MonthFromTime(result); return js_NewNumberValue(cx, result, rval); } static JSBool date_getDate(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) { jsdouble result; jsdouble *date = date_getProlog(cx, obj, argv); if (!date) return JS_FALSE; result = *date; if (!JSDOUBLE_IS_FINITE(result)) return js_NewNumberValue(cx, result, rval); result = LocalTime(result); result = DateFromTime(result); return js_NewNumberValue(cx, result, rval); } static JSBool date_getUTCDate(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) { jsdouble result; jsdouble *date = date_getProlog(cx, obj, argv); if (!date) return JS_FALSE; result = *date; if (!JSDOUBLE_IS_FINITE(result)) return js_NewNumberValue(cx, result, rval); result = DateFromTime(result); return js_NewNumberValue(cx, result, rval); } static JSBool date_getDay(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) { jsdouble result; jsdouble *date = date_getProlog(cx, obj, argv); if (!date) return JS_FALSE; result = *date; if (!JSDOUBLE_IS_FINITE(result)) return js_NewNumberValue(cx, result, rval); result = LocalTime(result); result = WeekDay(result); return js_NewNumberValue(cx, result, rval); } static JSBool date_getUTCDay(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) { jsdouble result; jsdouble *date = date_getProlog(cx, obj, argv); if (!date) return JS_FALSE; result = *date; if (!JSDOUBLE_IS_FINITE(result)) return js_NewNumberValue(cx, result, rval); result = WeekDay(result); return js_NewNumberValue(cx, result, rval); } static JSBool date_getHours(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) { jsdouble result; jsdouble *date = date_getProlog(cx, obj, argv); if (!date) return JS_FALSE; result = *date; if (!JSDOUBLE_IS_FINITE(result)) return js_NewNumberValue(cx, result, rval); result = HourFromTime(LocalTime(result)); return js_NewNumberValue(cx, result, rval); } static JSBool date_getUTCHours(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) { jsdouble result; jsdouble *date = date_getProlog(cx, obj, argv); if (!date) return JS_FALSE; result = *date; if (!JSDOUBLE_IS_FINITE(result)) return js_NewNumberValue(cx, result, rval); result = HourFromTime(result); return js_NewNumberValue(cx, result, rval); } static JSBool date_getMinutes(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) { jsdouble result; jsdouble *date = date_getProlog(cx, obj, argv); if (!date) return JS_FALSE; result = *date; if (!JSDOUBLE_IS_FINITE(result)) return js_NewNumberValue(cx, result, rval); result = MinFromTime(LocalTime(result)); return js_NewNumberValue(cx, result, rval); } static JSBool date_getUTCMinutes(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) { jsdouble result; jsdouble *date = date_getProlog(cx, obj, argv); if (!date) return JS_FALSE; result = *date; if (!JSDOUBLE_IS_FINITE(result)) return js_NewNumberValue(cx, result, rval); result = MinFromTime(result); return js_NewNumberValue(cx, result, rval); } /* Date.getSeconds is mapped to getUTCSeconds */ static JSBool date_getUTCSeconds(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) { jsdouble result; jsdouble *date = date_getProlog(cx, obj, argv); if (!date) return JS_FALSE; result = *date; if (!JSDOUBLE_IS_FINITE(result)) return js_NewNumberValue(cx, result, rval); result = SecFromTime(result); return js_NewNumberValue(cx, result, rval); } /* Date.getMilliseconds is mapped to getUTCMilliseconds */ static JSBool date_getUTCMilliseconds(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) { jsdouble result; jsdouble *date = date_getProlog(cx, obj, argv); if (!date) return JS_FALSE; result = *date; if (!JSDOUBLE_IS_FINITE(result)) return js_NewNumberValue(cx, result, rval); result = msFromTime(result); return js_NewNumberValue(cx, result, rval); } static JSBool date_getTimezoneOffset(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) { jsdouble result; jsdouble *date = date_getProlog(cx, obj, argv); if (!date) return JS_FALSE; result = *date; /* * Return the time zone offset in minutes for the current locale * that is appropriate for this time. This value would be a * constant except for daylight savings time. */ result = (result - LocalTime(result)) / msPerMinute; return js_NewNumberValue(cx, result, rval); } static JSBool date_setTime(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) { jsdouble result; jsdouble *date = date_getProlog(cx, obj, argv); if (!date) return JS_FALSE; if (!js_ValueToNumber(cx, argv[0], &result)) return JS_FALSE; result = TIMECLIP(result); *date = result; return js_NewNumberValue(cx, result, rval); } static JSBool date_makeTime(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, uintN maxargs, JSBool local, jsval *rval) { uintN i; jsdouble args[4], *argp, *stop; jsdouble hour, min, sec, msec; jsdouble lorutime; /* Local or UTC version of *date */ jsdouble msec_time; jsdouble result; jsdouble *date = date_getProlog(cx, obj, argv); if (!date) return JS_FALSE; result = *date; /* just return NaN if the date is already NaN */ if (!JSDOUBLE_IS_FINITE(result)) return js_NewNumberValue(cx, result, rval); /* Satisfy the ECMA rule that if a function is called with * fewer arguments than the specified formal arguments, the * remaining arguments are set to undefined. Seems like all * the Date.setWhatever functions in ECMA are only varargs * beyond the first argument; this should be set to undefined * if it's not given. This means that "d = new Date(); * d.setMilliseconds()" returns NaN. Blech. */ if (argc == 0) argc = 1; /* should be safe, because length of all setters is 1 */ else if (argc > maxargs) argc = maxargs; /* clamp argc */ for (i = 0; i < argc; i++) { if (!js_ValueToNumber(cx, argv[i], &args[i])) return JS_FALSE; if (!JSDOUBLE_IS_FINITE(args[i])) { *date = *cx->runtime->jsNaN; return js_NewNumberValue(cx, *date, rval); } args[i] = js_DoubleToInteger(args[i]); } if (local) lorutime = LocalTime(result); else lorutime = result; argp = args; stop = argp + argc; if (maxargs >= 4 && argp < stop) hour = *argp++; else hour = HourFromTime(lorutime); if (maxargs >= 3 && argp < stop) min = *argp++; else min = MinFromTime(lorutime); if (maxargs >= 2 && argp < stop) sec = *argp++; else sec = SecFromTime(lorutime); if (maxargs >= 1 && argp < stop) msec = *argp; else msec = msFromTime(lorutime); msec_time = MakeTime(hour, min, sec, msec); result = MakeDate(Day(lorutime), msec_time); /* fprintf(stderr, "%f\n", result); */ if (local) result = UTC(result); /* fprintf(stderr, "%f\n", result); */ *date = TIMECLIP(result); return js_NewNumberValue(cx, *date, rval); } static JSBool date_setMilliseconds(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) { return date_makeTime(cx, obj, argc, argv, 1, JS_TRUE, rval); } static JSBool date_setUTCMilliseconds(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) { return date_makeTime(cx, obj, argc, argv, 1, JS_FALSE, rval); } static JSBool date_setSeconds(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) { return date_makeTime(cx, obj, argc, argv, 2, JS_TRUE, rval); } static JSBool date_setUTCSeconds(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) { return date_makeTime(cx, obj, argc, argv, 2, JS_FALSE, rval); } static JSBool date_setMinutes(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) { return date_makeTime(cx, obj, argc, argv, 3, JS_TRUE, rval); } static JSBool date_setUTCMinutes(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) { return date_makeTime(cx, obj, argc, argv, 3, JS_FALSE, rval); } static JSBool date_setHours(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) { return date_makeTime(cx, obj, argc, argv, 4, JS_TRUE, rval); } static JSBool date_setUTCHours(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) { return date_makeTime(cx, obj, argc, argv, 4, JS_FALSE, rval); } static JSBool date_makeDate(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, uintN maxargs, JSBool local, jsval *rval) { uintN i; jsdouble lorutime; /* local or UTC version of *date */ jsdouble args[3], *argp, *stop; jsdouble year, month, day; jsdouble result; jsdouble *date = date_getProlog(cx, obj, argv); if (!date) return JS_FALSE; result = *date; /* see complaint about ECMA in date_MakeTime */ if (argc == 0) argc = 1; /* should be safe, because length of all setters is 1 */ else if (argc > maxargs) argc = maxargs; /* clamp argc */ for (i = 0; i < argc; i++) { if (!js_ValueToNumber(cx, argv[i], &args[i])) return JS_FALSE; if (!JSDOUBLE_IS_FINITE(args[i])) { *date = *cx->runtime->jsNaN; return js_NewNumberValue(cx, *date, rval); } args[i] = js_DoubleToInteger(args[i]); } /* return NaN if date is NaN and we're not setting the year, * If we are, use 0 as the time. */ if (!(JSDOUBLE_IS_FINITE(result))) { if (maxargs < 3) return js_NewNumberValue(cx, result, rval); else lorutime = +0.; } else { if (local) lorutime = LocalTime(result); else lorutime = result; } argp = args; stop = argp + argc; if (maxargs >= 3 && argp < stop) year = *argp++; else year = YearFromTime(lorutime); if (maxargs >= 2 && argp < stop) month = *argp++; else month = MonthFromTime(lorutime); if (maxargs >= 1 && argp < stop) day = *argp++; else day = DateFromTime(lorutime); day = MakeDay(year, month, day); /* day within year */ result = MakeDate(day, TimeWithinDay(lorutime)); if (local) result = UTC(result); *date = TIMECLIP(result); return js_NewNumberValue(cx, *date, rval); } static JSBool date_setDate(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) { return date_makeDate(cx, obj, argc, argv, 1, JS_TRUE, rval); } static JSBool date_setUTCDate(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) { return date_makeDate(cx, obj, argc, argv, 1, JS_FALSE, rval); } static JSBool date_setMonth(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) { return date_makeDate(cx, obj, argc, argv, 2, JS_TRUE, rval); } static JSBool date_setUTCMonth(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) { return date_makeDate(cx, obj, argc, argv, 2, JS_FALSE, rval); } static JSBool date_setFullYear(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) { return date_makeDate(cx, obj, argc, argv, 3, JS_TRUE, rval); } static JSBool date_setUTCFullYear(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) { return date_makeDate(cx, obj, argc, argv, 3, JS_FALSE, rval); } static JSBool date_setYear(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) { jsdouble t; jsdouble year; jsdouble day; jsdouble result; jsdouble *date = date_getProlog(cx, obj, argv); if (!date) return JS_FALSE; result = *date; if (!js_ValueToNumber(cx, argv[0], &year)) return JS_FALSE; if (!JSDOUBLE_IS_FINITE(year)) { *date = *cx->runtime->jsNaN; return js_NewNumberValue(cx, *date, rval); } year = js_DoubleToInteger(year); if (!JSDOUBLE_IS_FINITE(result)) { t = +0.0; } else { t = LocalTime(result); } if (year >= 0 && year <= 99) year += 1900; day = MakeDay(year, MonthFromTime(t), DateFromTime(t)); result = MakeDate(day, TimeWithinDay(t)); result = UTC(result); *date = TIMECLIP(result); return js_NewNumberValue(cx, *date, rval); } /* constants for toString, toUTCString */ static char js_NaN_date_str[] = "Invalid Date"; static const char* days[] = { "Sun","Mon","Tue","Wed","Thu","Fri","Sat" }; static const char* months[] = { "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec" }; static JSBool date_toGMTString(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) { char buf[100]; JSString *str; jsdouble *date = date_getProlog(cx, obj, argv); if (!date) return JS_FALSE; if (!JSDOUBLE_IS_FINITE(*date)) { JS_snprintf(buf, sizeof buf, js_NaN_date_str); } else { jsdouble temp = *date; /* Avoid dependence on PRMJ_FormatTimeUSEnglish, because it * requires a PRMJTime... which only has 16-bit years. Sub-ECMA. */ JS_snprintf(buf, sizeof buf, "%s, %.2d %s %.4d %.2d:%.2d:%.2d GMT", days[WeekDay(temp)], DateFromTime(temp), months[MonthFromTime(temp)], YearFromTime(temp), HourFromTime(temp), MinFromTime(temp), SecFromTime(temp)); } str = JS_NewStringCopyZ(cx, buf); if (!str) return JS_FALSE; *rval = STRING_TO_JSVAL(str); return JS_TRUE; } /* for Date.toLocaleString; interface to PRMJTime date struct. * If findEquivalent is true, then try to map the year to an equivalent year * that's in range. */ static void new_explode(jsdouble timeval, PRMJTime *split, JSBool findEquivalent) { jsint year = YearFromTime(timeval); int16 adjustedYear; /* If the year doesn't fit in a PRMJTime, find something to do about it. */ if (year > 32767 || year < -32768) { if (findEquivalent) { /* We're really just trying to get a timezone string; map the year * to some equivalent year in the range 0 to 2800. Borrowed from * A. D. Olsen. */ jsint cycles; #define CYCLE_YEARS 2800L cycles = (year >= 0) ? year / CYCLE_YEARS : -1 - (-1 - year) / CYCLE_YEARS; adjustedYear = (int16)(year - cycles * CYCLE_YEARS); } else { /* Clamp it to the nearest representable year. */ adjustedYear = (int16)((year > 0) ? 32767 : - 32768); } } else { adjustedYear = (int16)year; } split->tm_usec = (int32) msFromTime(timeval) * 1000; split->tm_sec = (int8) SecFromTime(timeval); split->tm_min = (int8) MinFromTime(timeval); split->tm_hour = (int8) HourFromTime(timeval); split->tm_mday = (int8) DateFromTime(timeval); split->tm_mon = (int8) MonthFromTime(timeval); split->tm_wday = (int8) WeekDay(timeval); split->tm_year = (int16) adjustedYear; split->tm_yday = (int16) DayWithinYear(timeval, year); /* not sure how this affects things, but it doesn't seem to matter. */ split->tm_isdst = (DaylightSavingTA(timeval) != 0); } typedef enum formatspec { FORMATSPEC_FULL, FORMATSPEC_DATE, FORMATSPEC_TIME } formatspec; /* helper function */ static JSBool date_format(JSContext *cx, jsdouble date, formatspec format, jsval *rval) { char buf[100]; JSString *str; char tzbuf[100]; JSBool usetz; size_t i, tzlen; PRMJTime split; if (!JSDOUBLE_IS_FINITE(date)) { JS_snprintf(buf, sizeof buf, js_NaN_date_str); } else { jsdouble local = LocalTime(date); /* offset from GMT in minutes. The offset includes daylight savings, if it applies. */ jsint minutes = (jsint) floor(AdjustTime(date) / msPerMinute); /* map 510 minutes to 0830 hours */ intN offset = (minutes / 60) * 100 + minutes % 60; /* print as "Wed Nov 05 19:38:03 GMT-0800 (PST) 1997" The TZA is * printed as 'GMT-0800' rather than as 'PST' to avoid * operating-system dependence on strftime (which * PRMJ_FormatTimeUSEnglish calls, for %Z only.) win32 prints * PST as 'Pacific Standard Time.' This way we always know * what we're getting, and can parse it if we produce it. * The OS TZA string is included as a comment. */ /* get a timezone string from the OS to include as a comment. */ new_explode(date, &split, JS_TRUE); if (PRMJ_FormatTime(tzbuf, sizeof tzbuf, "(%Z)", &split) != 0) { /* Decide whether to use the resulting timezone string. * * Reject it if it contains any non-ASCII, non-alphanumeric * characters. It's then likely in some other character * encoding, and we probably won't display it correctly. */ usetz = JS_TRUE; tzlen = strlen(tzbuf); if (tzlen > 100) { usetz = JS_FALSE; } else { for (i = 0; i < tzlen; i++) { jschar c = tzbuf[i]; if (c > 127 || !(isalpha(c) || isdigit(c) || c == ' ' || c == '(' || c == ')')) { usetz = JS_FALSE; } } } /* Also reject it if it's not parenthesized or if it's '()'. */ if (tzbuf[0] != '(' || tzbuf[1] == ')') usetz = JS_FALSE; } else usetz = JS_FALSE; switch (format) { case FORMATSPEC_FULL: /* * Avoid dependence on PRMJ_FormatTimeUSEnglish, because it * requires a PRMJTime... which only has 16-bit years. Sub-ECMA. */ /* Tue Oct 31 2000 09:41:40 GMT-0800 (PST) */ JS_snprintf(buf, sizeof buf, "%s %s %.2d %.4d %.2d:%.2d:%.2d GMT%+.4d%s%s", days[WeekDay(local)], months[MonthFromTime(local)], DateFromTime(local), YearFromTime(local), HourFromTime(local), MinFromTime(local), SecFromTime(local), offset, usetz ? " " : "", usetz ? tzbuf : ""); break; case FORMATSPEC_DATE: /* Tue Oct 31 2000 */ JS_snprintf(buf, sizeof buf, "%s %s %.2d %.4d", days[WeekDay(local)], months[MonthFromTime(local)], DateFromTime(local), YearFromTime(local)); break; case FORMATSPEC_TIME: /* 09:41:40 GMT-0800 (PST) */ JS_snprintf(buf, sizeof buf, "%.2d:%.2d:%.2d GMT%+.4d%s%s", HourFromTime(local), MinFromTime(local), SecFromTime(local), offset, usetz ? " " : "", usetz ? tzbuf : ""); break; } } str = JS_NewStringCopyZ(cx, buf); if (!str) return JS_FALSE; *rval = STRING_TO_JSVAL(str); return JS_TRUE; } static JSBool date_toLocaleHelper(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval, char *format) { char buf[100]; JSString *str; PRMJTime split; jsdouble *date = date_getProlog(cx, obj, argv); if (!date) return JS_FALSE; if (!JSDOUBLE_IS_FINITE(*date)) { JS_snprintf(buf, sizeof buf, js_NaN_date_str); } else { intN result_len; jsdouble local = LocalTime(*date); new_explode(local, &split, JS_FALSE); /* let PRMJTime format it. */ result_len = PRMJ_FormatTime(buf, sizeof buf, format, &split); /* If it failed, default to toString. */ if (result_len == 0) return date_format(cx, *date, FORMATSPEC_FULL, rval); /* Hacked check against undesired 2-digit year 00/00/00 form. */ if (strcmp(format, "%x") == 0 && result_len >= 6 && /* Format %x means use OS settings, which may have 2-digit yr, so hack end of 3/11/22 or 11.03.22 or 11Mar22 to use 4-digit yr...*/ !isdigit(buf[result_len - 3]) && isdigit(buf[result_len - 2]) && isdigit(buf[result_len - 1]) && /* ...but not if starts with 4-digit year, like 2022/3/11. */ !(isdigit(buf[0]) && isdigit(buf[1]) && isdigit(buf[2]) && isdigit(buf[3]))) { JS_snprintf(buf + (result_len - 2), (sizeof buf) - (result_len - 2), "%d", js_DateGetYear(cx, obj)); } } if (cx->localeCallbacks && cx->localeCallbacks->localeToUnicode) return cx->localeCallbacks->localeToUnicode(cx, buf, rval); str = JS_NewStringCopyZ(cx, buf); if (!str) return JS_FALSE; *rval = STRING_TO_JSVAL(str); return JS_TRUE; } static JSBool date_toLocaleString(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) { /* Use '%#c' for windows, because '%c' is * backward-compatible and non-y2k with msvc; '%#c' requests that a * full year be used in the result string. */ return date_toLocaleHelper(cx, obj, argc, argv, rval, #if defined(_WIN32) && !defined(__MWERKS__) "%#c" #else "%c" #endif ); } static JSBool date_toLocaleDateString(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) { /* Use '%#x' for windows, because '%x' is * backward-compatible and non-y2k with msvc; '%#x' requests that a * full year be used in the result string. */ return date_toLocaleHelper(cx, obj, argc, argv, rval, #if defined(_WIN32) && !defined(__MWERKS__) "%#x" #else "%x" #endif ); } static JSBool date_toLocaleTimeString(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) { return date_toLocaleHelper(cx, obj, argc, argv, rval, "%X"); } static JSBool date_toLocaleFormat(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) { JSString *fmt; if (argc == 0) return date_toLocaleString(cx, obj, argc, argv, rval); fmt = JS_ValueToString(cx, argv[0]); if (!fmt) return JS_FALSE; return date_toLocaleHelper(cx, obj, argc, argv, rval, JS_GetStringBytes(fmt)); } static JSBool date_toTimeString(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) { jsdouble *date = date_getProlog(cx, obj, argv); if (!date) return JS_FALSE; return date_format(cx, *date, FORMATSPEC_TIME, rval); } static JSBool date_toDateString(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) { jsdouble *date = date_getProlog(cx, obj, argv); if (!date) return JS_FALSE; return date_format(cx, *date, FORMATSPEC_DATE, rval); } #if JS_HAS_TOSOURCE #include #include "jsdtoa.h" static JSBool date_toSource(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) { jsdouble *date; char buf[DTOSTR_STANDARD_BUFFER_SIZE], *numStr, *bytes; JSString *str; date = date_getProlog(cx, obj, argv); if (!date) return JS_FALSE; numStr = JS_dtostr(buf, sizeof buf, DTOSTR_STANDARD, 0, *date); if (!numStr) { JS_ReportOutOfMemory(cx); return JS_FALSE; } bytes = JS_smprintf("(new %s(%s))", js_Date_str, numStr); if (!bytes) { JS_ReportOutOfMemory(cx); return JS_FALSE; } str = JS_NewString(cx, bytes, strlen(bytes)); if (!str) { free(bytes); return JS_FALSE; } *rval = STRING_TO_JSVAL(str); return JS_TRUE; } #endif static JSBool date_toString(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) { jsdouble *date = date_getProlog(cx, obj, argv); if (!date) return JS_FALSE; return date_format(cx, *date, FORMATSPEC_FULL, rval); } static JSBool date_valueOf(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) { /* It is an error to call date_valueOf on a non-date object, but we don't * need to check for that explicitly here because every path calls * date_getProlog, which does the check. */ /* If called directly with no arguments, convert to a time number. */ if (argc == 0) return date_getTime(cx, obj, argc, argv, rval); /* Convert to number only if the hint was given, otherwise favor string. */ if (argc == 1) { JSString *str, *str2; str = js_ValueToString(cx, argv[0]); if (!str) return JS_FALSE; str2 = ATOM_TO_STRING(cx->runtime->atomState.typeAtoms[JSTYPE_NUMBER]); if (js_EqualStrings(str, str2)) return date_getTime(cx, obj, argc, argv, rval); } return date_toString(cx, obj, argc, argv, rval); } /* * creation and destruction */ static JSFunctionSpec date_static_methods[] = { {"UTC", date_UTC, MAXARGS,0,0 }, {"parse", date_parse, 1,0,0 }, {"now", date_now, 0,0,0 }, {0,0,0,0,0} }; static JSFunctionSpec date_methods[] = { {"getTime", date_getTime, 0,0,0 }, {"getTimezoneOffset", date_getTimezoneOffset, 0,0,0 }, {"getYear", date_getYear, 0,0,0 }, {"getFullYear", date_getFullYear, 0,0,0 }, {"getUTCFullYear", date_getUTCFullYear, 0,0,0 }, {"getMonth", date_getMonth, 0,0,0 }, {"getUTCMonth", date_getUTCMonth, 0,0,0 }, {"getDate", date_getDate, 0,0,0 }, {"getUTCDate", date_getUTCDate, 0,0,0 }, {"getDay", date_getDay, 0,0,0 }, {"getUTCDay", date_getUTCDay, 0,0,0 }, {"getHours", date_getHours, 0,0,0 }, {"getUTCHours", date_getUTCHours, 0,0,0 }, {"getMinutes", date_getMinutes, 0,0,0 }, {"getUTCMinutes", date_getUTCMinutes, 0,0,0 }, {"getSeconds", date_getUTCSeconds, 0,0,0 }, {"getUTCSeconds", date_getUTCSeconds, 0,0,0 }, {"getMilliseconds", date_getUTCMilliseconds,0,0,0 }, {"getUTCMilliseconds", date_getUTCMilliseconds,0,0,0 }, {"setTime", date_setTime, 1,0,0 }, {"setYear", date_setYear, 1,0,0 }, {"setFullYear", date_setFullYear, 3,0,0 }, {"setUTCFullYear", date_setUTCFullYear, 3,0,0 }, {"setMonth", date_setMonth, 2,0,0 }, {"setUTCMonth", date_setUTCMonth, 2,0,0 }, {"setDate", date_setDate, 1,0,0 }, {"setUTCDate", date_setUTCDate, 1,0,0 }, {"setHours", date_setHours, 4,0,0 }, {"setUTCHours", date_setUTCHours, 4,0,0 }, {"setMinutes", date_setMinutes, 3,0,0 }, {"setUTCMinutes", date_setUTCMinutes, 3,0,0 }, {"setSeconds", date_setSeconds, 2,0,0 }, {"setUTCSeconds", date_setUTCSeconds, 2,0,0 }, {"setMilliseconds", date_setMilliseconds, 1,0,0 }, {"setUTCMilliseconds", date_setUTCMilliseconds,1,0,0 }, {"toUTCString", date_toGMTString, 0,0,0 }, {js_toLocaleString_str, date_toLocaleString, 0,0,0 }, {"toLocaleDateString", date_toLocaleDateString,0,0,0 }, {"toLocaleTimeString", date_toLocaleTimeString,0,0,0 }, {"toLocaleFormat", date_toLocaleFormat, 1,0,0 }, {"toDateString", date_toDateString, 0,0,0 }, {"toTimeString", date_toTimeString, 0,0,0 }, #if JS_HAS_TOSOURCE {js_toSource_str, date_toSource, 0,0,0 }, #endif {js_toString_str, date_toString, 0,0,0 }, {js_valueOf_str, date_valueOf, 0,0,0 }, {0,0,0,0,0} }; static jsdouble * date_constructor(JSContext *cx, JSObject* obj) { jsdouble *date; date = js_NewDouble(cx, 0.0, 0); if (!date) return NULL; OBJ_SET_SLOT(cx, obj, JSSLOT_PRIVATE, DOUBLE_TO_JSVAL(date)); return date; } static JSBool Date(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) { jsdouble *date; JSString *str; jsdouble d; /* Date called as function. */ if (!(cx->fp->flags & JSFRAME_CONSTRUCTING)) { int64 us, ms, us2ms; jsdouble msec_time; /* NSPR 2.0 docs say 'We do not support PRMJ_NowMS and PRMJ_NowS', * so compute ms from PRMJ_Now. */ us = PRMJ_Now(); JSLL_UI2L(us2ms, PRMJ_USEC_PER_MSEC); JSLL_DIV(ms, us, us2ms); JSLL_L2D(msec_time, ms); return date_format(cx, msec_time, FORMATSPEC_FULL, rval); } /* Date called as constructor. */ if (argc == 0) { int64 us, ms, us2ms; jsdouble msec_time; date = date_constructor(cx, obj); if (!date) return JS_FALSE; us = PRMJ_Now(); JSLL_UI2L(us2ms, PRMJ_USEC_PER_MSEC); JSLL_DIV(ms, us, us2ms); JSLL_L2D(msec_time, ms); *date = msec_time; } else if (argc == 1) { if (!JSVAL_IS_STRING(argv[0])) { /* the argument is a millisecond number */ if (!js_ValueToNumber(cx, argv[0], &d)) return JS_FALSE; date = date_constructor(cx, obj); if (!date) return JS_FALSE; *date = TIMECLIP(d); } else { /* the argument is a string; parse it. */ date = date_constructor(cx, obj); if (!date) return JS_FALSE; str = js_ValueToString(cx, argv[0]); if (!str) return JS_FALSE; if (!date_parseString(str, date)) *date = *cx->runtime->jsNaN; *date = TIMECLIP(*date); } } else { jsdouble array[MAXARGS]; uintN loop; jsdouble double_arg; jsdouble day; jsdouble msec_time; for (loop = 0; loop < MAXARGS; loop++) { if (loop < argc) { if (!js_ValueToNumber(cx, argv[loop], &double_arg)) return JS_FALSE; /* if any arg is NaN, make a NaN date object and return */ if (!JSDOUBLE_IS_FINITE(double_arg)) { date = date_constructor(cx, obj); if (!date) return JS_FALSE; *date = *cx->runtime->jsNaN; return JS_TRUE; } array[loop] = js_DoubleToInteger(double_arg); } else { if (loop == 2) { array[loop] = 1; /* Default the date argument to 1. */ } else { array[loop] = 0; } } } date = date_constructor(cx, obj); if (!date) return JS_FALSE; /* adjust 2-digit years into the 20th century */ if (array[0] >= 0 && array[0] <= 99) array[0] += 1900; day = MakeDay(array[0], array[1], array[2]); msec_time = MakeTime(array[3], array[4], array[5], array[6]); msec_time = MakeDate(day, msec_time); msec_time = UTC(msec_time); *date = TIMECLIP(msec_time); } return JS_TRUE; } JSObject * js_InitDateClass(JSContext *cx, JSObject *obj) { JSObject *proto; jsdouble *proto_date; /* set static LocalTZA */ LocalTZA = -(PRMJ_LocalGMTDifference() * msPerSecond); proto = JS_InitClass(cx, obj, NULL, &js_DateClass, Date, MAXARGS, NULL, date_methods, NULL, date_static_methods); if (!proto) return NULL; /* Alias toUTCString with toGMTString. (ECMA B.2.6) */ if (!JS_AliasProperty(cx, proto, "toUTCString", "toGMTString")) return NULL; /* Set the value of the Date.prototype date to NaN */ proto_date = date_constructor(cx, proto); if (!proto_date) return NULL; *proto_date = *cx->runtime->jsNaN; return proto; } JS_FRIEND_API(JSObject *) js_NewDateObjectMsec(JSContext *cx, jsdouble msec_time) { JSObject *obj; jsdouble *date; obj = js_NewObject(cx, &js_DateClass, NULL, NULL); if (!obj) return NULL; date = date_constructor(cx, obj); if (!date) return NULL; *date = msec_time; return obj; } JS_FRIEND_API(JSObject *) js_NewDateObject(JSContext* cx, int year, int mon, int mday, int hour, int min, int sec) { JSObject *obj; jsdouble msec_time; msec_time = date_msecFromDate(year, mon, mday, hour, min, sec, 0); obj = js_NewDateObjectMsec(cx, UTC(msec_time)); return obj; } JS_FRIEND_API(JSBool) js_DateIsValid(JSContext *cx, JSObject* obj) { jsdouble *date = date_getProlog(cx, obj, NULL); if (!date || JSDOUBLE_IS_NaN(*date)) return JS_FALSE; else return JS_TRUE; } JS_FRIEND_API(int) js_DateGetYear(JSContext *cx, JSObject* obj) { jsdouble *date = date_getProlog(cx, obj, NULL); /* Preserve legacy API behavior of returning 0 for invalid dates. */ if (!date || JSDOUBLE_IS_NaN(*date)) return 0; return (int) YearFromTime(LocalTime(*date)); } JS_FRIEND_API(int) js_DateGetMonth(JSContext *cx, JSObject* obj) { jsdouble *date = date_getProlog(cx, obj, NULL); if (!date || JSDOUBLE_IS_NaN(*date)) return 0; return (int) MonthFromTime(LocalTime(*date)); } JS_FRIEND_API(int) js_DateGetDate(JSContext *cx, JSObject* obj) { jsdouble *date = date_getProlog(cx, obj, NULL); if (!date || JSDOUBLE_IS_NaN(*date)) return 0; return (int) DateFromTime(LocalTime(*date)); } JS_FRIEND_API(int) js_DateGetHours(JSContext *cx, JSObject* obj) { jsdouble *date = date_getProlog(cx, obj, NULL); if (!date || JSDOUBLE_IS_NaN(*date)) return 0; return (int) HourFromTime(LocalTime(*date)); } JS_FRIEND_API(int) js_DateGetMinutes(JSContext *cx, JSObject* obj) { jsdouble *date = date_getProlog(cx, obj, NULL); if (!date || JSDOUBLE_IS_NaN(*date)) return 0; return (int) MinFromTime(LocalTime(*date)); } JS_FRIEND_API(int) js_DateGetSeconds(JSContext *cx, JSObject* obj) { jsdouble *date = date_getProlog(cx, obj, NULL); if (!date || JSDOUBLE_IS_NaN(*date)) return 0; return (int) SecFromTime(*date); } JS_FRIEND_API(void) js_DateSetYear(JSContext *cx, JSObject *obj, int year) { jsdouble local; jsdouble *date = date_getProlog(cx, obj, NULL); if (!date) return; local = LocalTime(*date); /* reset date if it was NaN */ if (JSDOUBLE_IS_NaN(local)) local = 0; local = date_msecFromDate(year, MonthFromTime(local), DateFromTime(local), HourFromTime(local), MinFromTime(local), SecFromTime(local), msFromTime(local)); *date = UTC(local); } JS_FRIEND_API(void) js_DateSetMonth(JSContext *cx, JSObject *obj, int month) { jsdouble local; jsdouble *date = date_getProlog(cx, obj, NULL); if (!date) return; local = LocalTime(*date); /* bail if date was NaN */ if (JSDOUBLE_IS_NaN(local)) return; local = date_msecFromDate(YearFromTime(local), month, DateFromTime(local), HourFromTime(local), MinFromTime(local), SecFromTime(local), msFromTime(local)); *date = UTC(local); } JS_FRIEND_API(void) js_DateSetDate(JSContext *cx, JSObject *obj, int date) { jsdouble local; jsdouble *datep = date_getProlog(cx, obj, NULL); if (!datep) return; local = LocalTime(*datep); if (JSDOUBLE_IS_NaN(local)) return; local = date_msecFromDate(YearFromTime(local), MonthFromTime(local), date, HourFromTime(local), MinFromTime(local), SecFromTime(local), msFromTime(local)); *datep = UTC(local); } JS_FRIEND_API(void) js_DateSetHours(JSContext *cx, JSObject *obj, int hours) { jsdouble local; jsdouble *date = date_getProlog(cx, obj, NULL); if (!date) return; local = LocalTime(*date); if (JSDOUBLE_IS_NaN(local)) return; local = date_msecFromDate(YearFromTime(local), MonthFromTime(local), DateFromTime(local), hours, MinFromTime(local), SecFromTime(local), msFromTime(local)); *date = UTC(local); } JS_FRIEND_API(void) js_DateSetMinutes(JSContext *cx, JSObject *obj, int minutes) { jsdouble local; jsdouble *date = date_getProlog(cx, obj, NULL); if (!date) return; local = LocalTime(*date); if (JSDOUBLE_IS_NaN(local)) return; local = date_msecFromDate(YearFromTime(local), MonthFromTime(local), DateFromTime(local), HourFromTime(local), minutes, SecFromTime(local), msFromTime(local)); *date = UTC(local); } JS_FRIEND_API(void) js_DateSetSeconds(JSContext *cx, JSObject *obj, int seconds) { jsdouble local; jsdouble *date = date_getProlog(cx, obj, NULL); if (!date) return; local = LocalTime(*date); if (JSDOUBLE_IS_NaN(local)) return; local = date_msecFromDate(YearFromTime(local), MonthFromTime(local), DateFromTime(local), HourFromTime(local), MinFromTime(local), seconds, msFromTime(local)); *date = UTC(local); } JS_FRIEND_API(jsdouble) js_DateGetMsecSinceEpoch(JSContext *cx, JSObject *obj) { jsdouble *date = date_getProlog(cx, obj, NULL); if (!date || JSDOUBLE_IS_NaN(*date)) return 0; return (*date); } pacparser-1.4.5/src/spidermonkey/js/src/jsdate.h000066400000000000000000000073011464010763600216370ustar00rootroot00000000000000/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- * * ***** BEGIN LICENSE BLOCK ***** * Version: MPL 1.1/GPL 2.0/LGPL 2.1 * * The contents of this file are subject to the Mozilla Public License Version * 1.1 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * http://www.mozilla.org/MPL/ * * Software distributed under the License is distributed on an "AS IS" basis, * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License * for the specific language governing rights and limitations under the * License. * * The Original Code is Mozilla Communicator client code, released * March 31, 1998. * * The Initial Developer of the Original Code is * Netscape Communications Corporation. * Portions created by the Initial Developer are Copyright (C) 1998 * the Initial Developer. All Rights Reserved. * * Contributor(s): * * Alternatively, the contents of this file may be used under the terms of * either of the GNU General Public License Version 2 or later (the "GPL"), * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), * in which case the provisions of the GPL or the LGPL are applicable instead * of those above. If you wish to allow use of your version of this file only * under the terms of either the GPL or the LGPL, and not to allow others to * use your version of this file under the terms of the MPL, indicate your * decision by deleting the provisions above and replace them with the notice * and other provisions required by the GPL or the LGPL. If you do not delete * the provisions above, a recipient may use your version of this file under * the terms of any one of the MPL, the GPL or the LGPL. * * ***** END LICENSE BLOCK ***** */ /* * JS Date class interface. */ #ifndef jsdate_h___ #define jsdate_h___ JS_BEGIN_EXTERN_C extern JSClass js_DateClass; extern JSObject * js_InitDateClass(JSContext *cx, JSObject *obj); /* * These functions provide a C interface to the date/time object */ /* * Construct a new Date Object from a time value given in milliseconds UTC * since the epoch. */ extern JS_FRIEND_API(JSObject*) js_NewDateObjectMsec(JSContext* cx, jsdouble msec_time); /* * Construct a new Date Object from an exploded local time value. */ extern JS_FRIEND_API(JSObject*) js_NewDateObject(JSContext* cx, int year, int mon, int mday, int hour, int min, int sec); /* * Detect whether the internal date value is NaN. (Because failure is * out-of-band for js_DateGet*) */ extern JS_FRIEND_API(JSBool) js_DateIsValid(JSContext *cx, JSObject* obj); extern JS_FRIEND_API(int) js_DateGetYear(JSContext *cx, JSObject* obj); extern JS_FRIEND_API(int) js_DateGetMonth(JSContext *cx, JSObject* obj); extern JS_FRIEND_API(int) js_DateGetDate(JSContext *cx, JSObject* obj); extern JS_FRIEND_API(int) js_DateGetHours(JSContext *cx, JSObject* obj); extern JS_FRIEND_API(int) js_DateGetMinutes(JSContext *cx, JSObject* obj); extern JS_FRIEND_API(int) js_DateGetSeconds(JSContext *cx, JSObject* obj); extern JS_FRIEND_API(void) js_DateSetYear(JSContext *cx, JSObject *obj, int year); extern JS_FRIEND_API(void) js_DateSetMonth(JSContext *cx, JSObject *obj, int year); extern JS_FRIEND_API(void) js_DateSetDate(JSContext *cx, JSObject *obj, int date); extern JS_FRIEND_API(void) js_DateSetHours(JSContext *cx, JSObject *obj, int hours); extern JS_FRIEND_API(void) js_DateSetMinutes(JSContext *cx, JSObject *obj, int minutes); extern JS_FRIEND_API(void) js_DateSetSeconds(JSContext *cx, JSObject *obj, int seconds); extern JS_FRIEND_API(jsdouble) js_DateGetMsecSinceEpoch(JSContext *cx, JSObject *obj); JS_END_EXTERN_C #endif /* jsdate_h___ */ pacparser-1.4.5/src/spidermonkey/js/src/jsdbgapi.c000066400000000000000000001173131464010763600221500ustar00rootroot00000000000000/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- * vim: set ts=8 sw=4 et tw=78: * * ***** BEGIN LICENSE BLOCK ***** * Version: MPL 1.1/GPL 2.0/LGPL 2.1 * * The contents of this file are subject to the Mozilla Public License Version * 1.1 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * http://www.mozilla.org/MPL/ * * Software distributed under the License is distributed on an "AS IS" basis, * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License * for the specific language governing rights and limitations under the * License. * * The Original Code is Mozilla Communicator client code, released * March 31, 1998. * * The Initial Developer of the Original Code is * Netscape Communications Corporation. * Portions created by the Initial Developer are Copyright (C) 1998 * the Initial Developer. All Rights Reserved. * * Contributor(s): * * Alternatively, the contents of this file may be used under the terms of * either of the GNU General Public License Version 2 or later (the "GPL"), * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), * in which case the provisions of the GPL or the LGPL are applicable instead * of those above. If you wish to allow use of your version of this file only * under the terms of either the GPL or the LGPL, and not to allow others to * use your version of this file under the terms of the MPL, indicate your * decision by deleting the provisions above and replace them with the notice * and other provisions required by the GPL or the LGPL. If you do not delete * the provisions above, a recipient may use your version of this file under * the terms of any one of the MPL, the GPL or the LGPL. * * ***** END LICENSE BLOCK ***** */ /* * JS debugging API. */ #include "jsstddef.h" #include #include "jstypes.h" #include "jsutil.h" /* Added by JSIFY */ #include "jsclist.h" #include "jsapi.h" #include "jscntxt.h" #include "jsconfig.h" #include "jsdbgapi.h" #include "jsfun.h" #include "jsgc.h" #include "jsinterp.h" #include "jslock.h" #include "jsobj.h" #include "jsopcode.h" #include "jsscope.h" #include "jsscript.h" #include "jsstr.h" typedef struct JSTrap { JSCList links; JSScript *script; jsbytecode *pc; JSOp op; JSTrapHandler handler; void *closure; } JSTrap; static JSTrap * FindTrap(JSRuntime *rt, JSScript *script, jsbytecode *pc) { JSTrap *trap; for (trap = (JSTrap *)rt->trapList.next; trap != (JSTrap *)&rt->trapList; trap = (JSTrap *)trap->links.next) { if (trap->script == script && trap->pc == pc) return trap; } return NULL; } void js_PatchOpcode(JSContext *cx, JSScript *script, jsbytecode *pc, JSOp op) { JSTrap *trap; trap = FindTrap(cx->runtime, script, pc); if (trap) trap->op = op; else *pc = (jsbytecode)op; } JS_PUBLIC_API(JSBool) JS_SetTrap(JSContext *cx, JSScript *script, jsbytecode *pc, JSTrapHandler handler, void *closure) { JSRuntime *rt; JSTrap *trap; rt = cx->runtime; trap = FindTrap(rt, script, pc); if (trap) { JS_ASSERT(trap->script == script && trap->pc == pc); JS_ASSERT(*pc == JSOP_TRAP); } else { trap = (JSTrap *) JS_malloc(cx, sizeof *trap); if (!trap || !js_AddRoot(cx, &trap->closure, "trap->closure")) { if (trap) JS_free(cx, trap); return JS_FALSE; } JS_APPEND_LINK(&trap->links, &rt->trapList); trap->script = script; trap->pc = pc; trap->op = (JSOp)*pc; *pc = JSOP_TRAP; } trap->handler = handler; trap->closure = closure; return JS_TRUE; } JS_PUBLIC_API(JSOp) JS_GetTrapOpcode(JSContext *cx, JSScript *script, jsbytecode *pc) { JSTrap *trap; trap = FindTrap(cx->runtime, script, pc); if (!trap) { JS_ASSERT(0); /* XXX can't happen */ return JSOP_LIMIT; } return trap->op; } static void DestroyTrap(JSContext *cx, JSTrap *trap) { JS_REMOVE_LINK(&trap->links); *trap->pc = (jsbytecode)trap->op; js_RemoveRoot(cx->runtime, &trap->closure); JS_free(cx, trap); } JS_PUBLIC_API(void) JS_ClearTrap(JSContext *cx, JSScript *script, jsbytecode *pc, JSTrapHandler *handlerp, void **closurep) { JSTrap *trap; trap = FindTrap(cx->runtime, script, pc); if (handlerp) *handlerp = trap ? trap->handler : NULL; if (closurep) *closurep = trap ? trap->closure : NULL; if (trap) DestroyTrap(cx, trap); } JS_PUBLIC_API(void) JS_ClearScriptTraps(JSContext *cx, JSScript *script) { JSRuntime *rt; JSTrap *trap, *next; rt = cx->runtime; for (trap = (JSTrap *)rt->trapList.next; trap != (JSTrap *)&rt->trapList; trap = next) { next = (JSTrap *)trap->links.next; if (trap->script == script) DestroyTrap(cx, trap); } } JS_PUBLIC_API(void) JS_ClearAllTraps(JSContext *cx) { JSRuntime *rt; JSTrap *trap, *next; rt = cx->runtime; for (trap = (JSTrap *)rt->trapList.next; trap != (JSTrap *)&rt->trapList; trap = next) { next = (JSTrap *)trap->links.next; DestroyTrap(cx, trap); } } JS_PUBLIC_API(JSTrapStatus) JS_HandleTrap(JSContext *cx, JSScript *script, jsbytecode *pc, jsval *rval) { JSTrap *trap; JSTrapStatus status; jsint op; trap = FindTrap(cx->runtime, script, pc); if (!trap) { JS_ASSERT(0); /* XXX can't happen */ return JSTRAP_ERROR; } /* * It's important that we not use 'trap->' after calling the callback -- * the callback might remove the trap! */ op = (jsint)trap->op; status = trap->handler(cx, script, pc, rval, trap->closure); if (status == JSTRAP_CONTINUE) { /* By convention, return the true op to the interpreter in rval. */ *rval = INT_TO_JSVAL(op); } return status; } JS_PUBLIC_API(JSBool) JS_SetInterrupt(JSRuntime *rt, JSTrapHandler handler, void *closure) { rt->interruptHandler = handler; rt->interruptHandlerData = closure; return JS_TRUE; } JS_PUBLIC_API(JSBool) JS_ClearInterrupt(JSRuntime *rt, JSTrapHandler *handlerp, void **closurep) { if (handlerp) *handlerp = (JSTrapHandler)rt->interruptHandler; if (closurep) *closurep = rt->interruptHandlerData; rt->interruptHandler = 0; rt->interruptHandlerData = 0; return JS_TRUE; } /************************************************************************/ typedef struct JSWatchPoint { JSCList links; JSObject *object; /* weak link, see js_FinalizeObject */ JSScopeProperty *sprop; JSPropertyOp setter; JSWatchPointHandler handler; void *closure; uintN flags; } JSWatchPoint; #define JSWP_LIVE 0x1 /* live because set and not cleared */ #define JSWP_HELD 0x2 /* held while running handler/setter */ static JSBool DropWatchPoint(JSContext *cx, JSWatchPoint *wp, uintN flag) { JSBool ok; JSScopeProperty *sprop; JSObject *pobj; JSProperty *prop; JSPropertyOp setter; ok = JS_TRUE; wp->flags &= ~flag; if (wp->flags != 0) return JS_TRUE; /* * Remove wp from the list, then if there are no other watchpoints for * wp->sprop in any scope, restore wp->sprop->setter from wp. */ JS_REMOVE_LINK(&wp->links); sprop = wp->sprop; /* * If js_ChangeNativePropertyAttrs fails, propagate failure after removing * wp->closure's root and freeing wp. */ setter = js_GetWatchedSetter(cx->runtime, NULL, sprop); if (!setter) { ok = js_LookupProperty(cx, wp->object, sprop->id, &pobj, &prop); /* * If the property wasn't found on wp->object or didn't exist, then * someone else has dealt with this sprop, and we don't need to change * the property attributes. */ if (ok && prop) { if (pobj == wp->object) { JS_ASSERT(OBJ_SCOPE(pobj)->object == pobj); sprop = js_ChangeScopePropertyAttrs(cx, OBJ_SCOPE(pobj), sprop, 0, sprop->attrs, sprop->getter, wp->setter); if (!sprop) ok = JS_FALSE; } OBJ_DROP_PROPERTY(cx, pobj, prop); } } js_RemoveRoot(cx->runtime, &wp->closure); JS_free(cx, wp); return ok; } void js_MarkWatchPoints(JSContext *cx) { JSRuntime *rt; JSWatchPoint *wp; rt = cx->runtime; for (wp = (JSWatchPoint *)rt->watchPointList.next; wp != (JSWatchPoint *)&rt->watchPointList; wp = (JSWatchPoint *)wp->links.next) { MARK_SCOPE_PROPERTY(cx, wp->sprop); if (wp->sprop->attrs & JSPROP_SETTER) JS_MarkGCThing(cx, wp->setter, "wp->setter", NULL); } } static JSWatchPoint * FindWatchPoint(JSRuntime *rt, JSScope *scope, jsid id) { JSWatchPoint *wp; for (wp = (JSWatchPoint *)rt->watchPointList.next; wp != (JSWatchPoint *)&rt->watchPointList; wp = (JSWatchPoint *)wp->links.next) { if (wp->object == scope->object && wp->sprop->id == id) return wp; } return NULL; } JSScopeProperty * js_FindWatchPoint(JSRuntime *rt, JSScope *scope, jsid id) { JSWatchPoint *wp; wp = FindWatchPoint(rt, scope, id); if (!wp) return NULL; return wp->sprop; } JSPropertyOp js_GetWatchedSetter(JSRuntime *rt, JSScope *scope, const JSScopeProperty *sprop) { JSWatchPoint *wp; for (wp = (JSWatchPoint *)rt->watchPointList.next; wp != (JSWatchPoint *)&rt->watchPointList; wp = (JSWatchPoint *)wp->links.next) { if ((!scope || wp->object == scope->object) && wp->sprop == sprop) return wp->setter; } return NULL; } JSBool JS_DLL_CALLBACK js_watch_set(JSContext *cx, JSObject *obj, jsval id, jsval *vp) { JSRuntime *rt; JSWatchPoint *wp; JSScopeProperty *sprop; jsval propid, userid; JSScope *scope; JSBool ok; rt = cx->runtime; for (wp = (JSWatchPoint *)rt->watchPointList.next; wp != (JSWatchPoint *)&rt->watchPointList; wp = (JSWatchPoint *)wp->links.next) { sprop = wp->sprop; if (wp->object == obj && SPROP_USERID(sprop) == id && !(wp->flags & JSWP_HELD)) { wp->flags |= JSWP_HELD; JS_LOCK_OBJ(cx, obj); propid = ID_TO_VALUE(sprop->id); userid = (sprop->flags & SPROP_HAS_SHORTID) ? INT_TO_JSVAL(sprop->shortid) : propid; scope = OBJ_SCOPE(obj); JS_UNLOCK_OBJ(cx, obj); /* NB: wp is held, so we can safely dereference it still. */ ok = wp->handler(cx, obj, propid, SPROP_HAS_VALID_SLOT(sprop, scope) ? OBJ_GET_SLOT(cx, obj, sprop->slot) : JSVAL_VOID, vp, wp->closure); if (ok) { /* * Create a pseudo-frame for the setter invocation so that any * stack-walking security code under the setter will correctly * identify the guilty party. So that the watcher appears to * be active to obj_eval and other such code, point frame.pc * at the JSOP_STOP at the end of the script. */ JSObject *closure; JSClass *clasp; JSFunction *fun; JSScript *script; uintN nslots; jsval smallv[5]; jsval *argv; JSStackFrame frame; closure = (JSObject *) wp->closure; clasp = OBJ_GET_CLASS(cx, closure); if (clasp == &js_FunctionClass) { fun = (JSFunction *) JS_GetPrivate(cx, closure); script = FUN_SCRIPT(fun); } else if (clasp == &js_ScriptClass) { fun = NULL; script = (JSScript *) JS_GetPrivate(cx, closure); } else { fun = NULL; script = NULL; } nslots = 2; if (fun) { nslots += fun->nargs; if (FUN_NATIVE(fun)) nslots += fun->u.n.extra; } if (nslots <= JS_ARRAY_LENGTH(smallv)) { argv = smallv; } else { argv = JS_malloc(cx, nslots * sizeof(jsval)); if (!argv) { DropWatchPoint(cx, wp, JSWP_HELD); return JS_FALSE; } } argv[0] = OBJECT_TO_JSVAL(closure); argv[1] = JSVAL_NULL; memset(argv + 2, 0, (nslots - 2) * sizeof(jsval)); memset(&frame, 0, sizeof(frame)); frame.script = script; if (script) { JS_ASSERT(script->length >= JSOP_STOP_LENGTH); frame.pc = script->code + script->length - JSOP_STOP_LENGTH; } frame.fun = fun; frame.argv = argv + 2; frame.down = cx->fp; frame.scopeChain = OBJ_GET_PARENT(cx, closure); cx->fp = &frame; ok = !wp->setter || ((sprop->attrs & JSPROP_SETTER) ? js_InternalCall(cx, obj, OBJECT_TO_JSVAL(wp->setter), 1, vp, vp) : wp->setter(cx, OBJ_THIS_OBJECT(cx, obj), userid, vp)); cx->fp = frame.down; if (argv != smallv) JS_free(cx, argv); } return DropWatchPoint(cx, wp, JSWP_HELD) && ok; } } return JS_TRUE; } JSBool JS_DLL_CALLBACK js_watch_set_wrapper(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) { JSObject *funobj; JSFunction *wrapper; jsval userid; funobj = JSVAL_TO_OBJECT(argv[-2]); JS_ASSERT(OBJ_GET_CLASS(cx, funobj) == &js_FunctionClass); wrapper = (JSFunction *) JS_GetPrivate(cx, funobj); userid = ATOM_KEY(wrapper->atom); *rval = argv[0]; return js_watch_set(cx, obj, userid, rval); } JSPropertyOp js_WrapWatchedSetter(JSContext *cx, jsid id, uintN attrs, JSPropertyOp setter) { JSAtom *atom; JSFunction *wrapper; if (!(attrs & JSPROP_SETTER)) return &js_watch_set; /* & to silence schoolmarmish MSVC */ if (JSID_IS_ATOM(id)) { atom = JSID_TO_ATOM(id); } else if (JSID_IS_INT(id)) { atom = js_AtomizeInt(cx, JSID_TO_INT(id), 0); if (!atom) return NULL; } else { atom = NULL; } wrapper = js_NewFunction(cx, NULL, js_watch_set_wrapper, 1, 0, OBJ_GET_PARENT(cx, (JSObject *)setter), atom); if (!wrapper) return NULL; return (JSPropertyOp) wrapper->object; } JS_PUBLIC_API(JSBool) JS_SetWatchPoint(JSContext *cx, JSObject *obj, jsval id, JSWatchPointHandler handler, void *closure) { JSAtom *atom; jsid propid; JSObject *pobj; JSProperty *prop; JSScopeProperty *sprop; JSRuntime *rt; JSBool ok; JSWatchPoint *wp; JSPropertyOp watcher; if (!OBJ_IS_NATIVE(obj)) { JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_CANT_WATCH, OBJ_GET_CLASS(cx, obj)->name); return JS_FALSE; } if (JSVAL_IS_INT(id)) { propid = INT_JSVAL_TO_JSID(id); atom = NULL; } else { atom = js_ValueToStringAtom(cx, id); if (!atom) return JS_FALSE; propid = ATOM_TO_JSID(atom); } if (!js_LookupProperty(cx, obj, propid, &pobj, &prop)) return JS_FALSE; sprop = (JSScopeProperty *) prop; rt = cx->runtime; if (!sprop) { /* Check for a deleted symbol watchpoint, which holds its property. */ sprop = js_FindWatchPoint(rt, OBJ_SCOPE(obj), propid); if (!sprop) { /* Make a new property in obj so we can watch for the first set. */ if (!js_DefineProperty(cx, obj, propid, JSVAL_VOID, NULL, NULL, JSPROP_ENUMERATE, &prop)) { return JS_FALSE; } sprop = (JSScopeProperty *) prop; } } else if (pobj != obj) { /* Clone the prototype property so we can watch the right object. */ jsval value; JSPropertyOp getter, setter; uintN attrs, flags; intN shortid; if (OBJ_IS_NATIVE(pobj)) { value = SPROP_HAS_VALID_SLOT(sprop, OBJ_SCOPE(pobj)) ? LOCKED_OBJ_GET_SLOT(pobj, sprop->slot) : JSVAL_VOID; getter = sprop->getter; setter = sprop->setter; attrs = sprop->attrs; flags = sprop->flags; shortid = sprop->shortid; } else { if (!OBJ_GET_PROPERTY(cx, pobj, id, &value) || !OBJ_GET_ATTRIBUTES(cx, pobj, id, prop, &attrs)) { OBJ_DROP_PROPERTY(cx, pobj, prop); return JS_FALSE; } getter = setter = NULL; flags = 0; shortid = 0; } OBJ_DROP_PROPERTY(cx, pobj, prop); /* Recall that obj is native, whether or not pobj is native. */ if (!js_DefineNativeProperty(cx, obj, propid, value, getter, setter, attrs, flags, shortid, &prop)) { return JS_FALSE; } sprop = (JSScopeProperty *) prop; } /* * At this point, prop/sprop exists in obj, obj is locked, and we must * OBJ_DROP_PROPERTY(cx, obj, prop) before returning. */ ok = JS_TRUE; wp = FindWatchPoint(rt, OBJ_SCOPE(obj), propid); if (!wp) { watcher = js_WrapWatchedSetter(cx, propid, sprop->attrs, sprop->setter); if (!watcher) { ok = JS_FALSE; goto out; } wp = (JSWatchPoint *) JS_malloc(cx, sizeof *wp); if (!wp) { ok = JS_FALSE; goto out; } wp->handler = NULL; wp->closure = NULL; ok = js_AddRoot(cx, &wp->closure, "wp->closure"); if (!ok) { JS_free(cx, wp); goto out; } wp->object = obj; JS_ASSERT(sprop->setter != js_watch_set || pobj != obj); wp->setter = sprop->setter; wp->flags = JSWP_LIVE; /* XXXbe nest in obj lock here */ sprop = js_ChangeNativePropertyAttrs(cx, obj, sprop, 0, sprop->attrs, sprop->getter, watcher); if (!sprop) { /* Self-link so DropWatchPoint can JS_REMOVE_LINK it. */ JS_INIT_CLIST(&wp->links); DropWatchPoint(cx, wp, JSWP_LIVE); ok = JS_FALSE; goto out; } wp->sprop = sprop; /* * Now that wp is fully initialized, append it to rt's wp list. * Because obj is locked we know that no other thread could have added * a watchpoint for (obj, propid). */ JS_ASSERT(!FindWatchPoint(rt, OBJ_SCOPE(obj), propid)); JS_APPEND_LINK(&wp->links, &rt->watchPointList); } wp->handler = handler; wp->closure = closure; out: OBJ_DROP_PROPERTY(cx, obj, prop); return ok; } JS_PUBLIC_API(JSBool) JS_ClearWatchPoint(JSContext *cx, JSObject *obj, jsval id, JSWatchPointHandler *handlerp, void **closurep) { JSRuntime *rt; JSWatchPoint *wp; rt = cx->runtime; for (wp = (JSWatchPoint *)rt->watchPointList.next; wp != (JSWatchPoint *)&rt->watchPointList; wp = (JSWatchPoint *)wp->links.next) { if (wp->object == obj && SPROP_USERID(wp->sprop) == id) { if (handlerp) *handlerp = wp->handler; if (closurep) *closurep = wp->closure; return DropWatchPoint(cx, wp, JSWP_LIVE); } } if (handlerp) *handlerp = NULL; if (closurep) *closurep = NULL; return JS_TRUE; } JS_PUBLIC_API(JSBool) JS_ClearWatchPointsForObject(JSContext *cx, JSObject *obj) { JSRuntime *rt; JSWatchPoint *wp, *next; rt = cx->runtime; for (wp = (JSWatchPoint *)rt->watchPointList.next; wp != (JSWatchPoint *)&rt->watchPointList; wp = next) { next = (JSWatchPoint *)wp->links.next; if (wp->object == obj && !DropWatchPoint(cx, wp, JSWP_LIVE)) return JS_FALSE; } return JS_TRUE; } JS_PUBLIC_API(JSBool) JS_ClearAllWatchPoints(JSContext *cx) { JSRuntime *rt; JSWatchPoint *wp, *next; rt = cx->runtime; for (wp = (JSWatchPoint *)rt->watchPointList.next; wp != (JSWatchPoint *)&rt->watchPointList; wp = next) { next = (JSWatchPoint *)wp->links.next; if (!DropWatchPoint(cx, wp, JSWP_LIVE)) return JS_FALSE; } return JS_TRUE; } /************************************************************************/ JS_PUBLIC_API(uintN) JS_PCToLineNumber(JSContext *cx, JSScript *script, jsbytecode *pc) { return js_PCToLineNumber(cx, script, pc); } JS_PUBLIC_API(jsbytecode *) JS_LineNumberToPC(JSContext *cx, JSScript *script, uintN lineno) { return js_LineNumberToPC(script, lineno); } JS_PUBLIC_API(JSScript *) JS_GetFunctionScript(JSContext *cx, JSFunction *fun) { return FUN_SCRIPT(fun); } JS_PUBLIC_API(JSNative) JS_GetFunctionNative(JSContext *cx, JSFunction *fun) { return FUN_NATIVE(fun); } JS_PUBLIC_API(JSPrincipals *) JS_GetScriptPrincipals(JSContext *cx, JSScript *script) { return script->principals; } /************************************************************************/ /* * Stack Frame Iterator */ JS_PUBLIC_API(JSStackFrame *) JS_FrameIterator(JSContext *cx, JSStackFrame **iteratorp) { *iteratorp = (*iteratorp == NULL) ? cx->fp : (*iteratorp)->down; return *iteratorp; } JS_PUBLIC_API(JSScript *) JS_GetFrameScript(JSContext *cx, JSStackFrame *fp) { return fp->script; } JS_PUBLIC_API(jsbytecode *) JS_GetFramePC(JSContext *cx, JSStackFrame *fp) { return fp->pc; } JS_PUBLIC_API(JSStackFrame *) JS_GetScriptedCaller(JSContext *cx, JSStackFrame *fp) { if (!fp) fp = cx->fp; while ((fp = fp->down) != NULL) { if (fp->script) return fp; } return NULL; } JS_PUBLIC_API(JSPrincipals *) JS_StackFramePrincipals(JSContext *cx, JSStackFrame *fp) { if (fp->fun) { JSRuntime *rt = cx->runtime; if (rt->findObjectPrincipals) { JSObject *callee = JSVAL_TO_OBJECT(fp->argv[-2]); if (fp->fun->object != callee) return rt->findObjectPrincipals(cx, callee); /* FALL THROUGH */ } } if (fp->script) return fp->script->principals; return NULL; } JS_PUBLIC_API(JSPrincipals *) JS_EvalFramePrincipals(JSContext *cx, JSStackFrame *fp, JSStackFrame *caller) { JSRuntime *rt; JSObject *callee; JSPrincipals *principals, *callerPrincipals; rt = cx->runtime; if (rt->findObjectPrincipals) { callee = JSVAL_TO_OBJECT(fp->argv[-2]); principals = rt->findObjectPrincipals(cx, callee); } else { principals = NULL; } if (!caller) return principals; callerPrincipals = JS_StackFramePrincipals(cx, caller); return (callerPrincipals && principals && callerPrincipals->subsume(callerPrincipals, principals)) ? principals : callerPrincipals; } JS_PUBLIC_API(void *) JS_GetFrameAnnotation(JSContext *cx, JSStackFrame *fp) { if (fp->annotation && fp->script) { JSPrincipals *principals = JS_StackFramePrincipals(cx, fp); if (principals && principals->globalPrivilegesEnabled(cx, principals)) { /* * Give out an annotation only if privileges have not been revoked * or disabled globally. */ return fp->annotation; } } return NULL; } JS_PUBLIC_API(void) JS_SetFrameAnnotation(JSContext *cx, JSStackFrame *fp, void *annotation) { fp->annotation = annotation; } JS_PUBLIC_API(void *) JS_GetFramePrincipalArray(JSContext *cx, JSStackFrame *fp) { JSPrincipals *principals; principals = JS_StackFramePrincipals(cx, fp); if (!principals) return NULL; return principals->getPrincipalArray(cx, principals); } JS_PUBLIC_API(JSBool) JS_IsNativeFrame(JSContext *cx, JSStackFrame *fp) { return !fp->script; } /* this is deprecated, use JS_GetFrameScopeChain instead */ JS_PUBLIC_API(JSObject *) JS_GetFrameObject(JSContext *cx, JSStackFrame *fp) { return fp->scopeChain; } JS_PUBLIC_API(JSObject *) JS_GetFrameScopeChain(JSContext *cx, JSStackFrame *fp) { /* Force creation of argument and call objects if not yet created */ (void) JS_GetFrameCallObject(cx, fp); return js_GetScopeChain(cx, fp); } JS_PUBLIC_API(JSObject *) JS_GetFrameCallObject(JSContext *cx, JSStackFrame *fp) { if (! fp->fun) return NULL; /* Force creation of argument object if not yet created */ (void) js_GetArgsObject(cx, fp); /* * XXX ill-defined: null return here means error was reported, unlike a * null returned above or in the #else */ return js_GetCallObject(cx, fp, NULL); } JS_PUBLIC_API(JSObject *) JS_GetFrameThis(JSContext *cx, JSStackFrame *fp) { return fp->thisp; } JS_PUBLIC_API(JSFunction *) JS_GetFrameFunction(JSContext *cx, JSStackFrame *fp) { return fp->fun; } JS_PUBLIC_API(JSObject *) JS_GetFrameFunctionObject(JSContext *cx, JSStackFrame *fp) { return fp->argv && fp->fun ? JSVAL_TO_OBJECT(fp->argv[-2]) : NULL; } JS_PUBLIC_API(JSBool) JS_IsConstructorFrame(JSContext *cx, JSStackFrame *fp) { return (fp->flags & JSFRAME_CONSTRUCTING) != 0; } JS_PUBLIC_API(JSObject *) JS_GetFrameCalleeObject(JSContext *cx, JSStackFrame *fp) { return fp->argv ? JSVAL_TO_OBJECT(fp->argv[-2]) : NULL; } JS_PUBLIC_API(JSBool) JS_IsDebuggerFrame(JSContext *cx, JSStackFrame *fp) { return (fp->flags & JSFRAME_DEBUGGER) != 0; } JS_PUBLIC_API(jsval) JS_GetFrameReturnValue(JSContext *cx, JSStackFrame *fp) { return fp->rval; } JS_PUBLIC_API(void) JS_SetFrameReturnValue(JSContext *cx, JSStackFrame *fp, jsval rval) { fp->rval = rval; } /************************************************************************/ JS_PUBLIC_API(const char *) JS_GetScriptFilename(JSContext *cx, JSScript *script) { return script->filename; } JS_PUBLIC_API(uintN) JS_GetScriptBaseLineNumber(JSContext *cx, JSScript *script) { return script->lineno; } JS_PUBLIC_API(uintN) JS_GetScriptLineExtent(JSContext *cx, JSScript *script) { return js_GetScriptLineExtent(script); } JS_PUBLIC_API(JSVersion) JS_GetScriptVersion(JSContext *cx, JSScript *script) { return script->version & JSVERSION_MASK; } /***************************************************************************/ JS_PUBLIC_API(void) JS_SetNewScriptHook(JSRuntime *rt, JSNewScriptHook hook, void *callerdata) { rt->newScriptHook = hook; rt->newScriptHookData = callerdata; } JS_PUBLIC_API(void) JS_SetDestroyScriptHook(JSRuntime *rt, JSDestroyScriptHook hook, void *callerdata) { rt->destroyScriptHook = hook; rt->destroyScriptHookData = callerdata; } /***************************************************************************/ JS_PUBLIC_API(JSBool) JS_EvaluateUCInStackFrame(JSContext *cx, JSStackFrame *fp, const jschar *chars, uintN length, const char *filename, uintN lineno, jsval *rval) { JSObject *scobj; uint32 flags, options; JSScript *script; JSBool ok; scobj = JS_GetFrameScopeChain(cx, fp); if (!scobj) return JS_FALSE; /* * XXX Hack around ancient compiler API to propagate the JSFRAME_SPECIAL * flags to the code generator (see js_EmitTree's TOK_SEMI case). */ flags = fp->flags; fp->flags |= JSFRAME_DEBUGGER | JSFRAME_EVAL; options = cx->options; cx->options = options | JSOPTION_COMPILE_N_GO; script = JS_CompileUCScriptForPrincipals(cx, scobj, JS_StackFramePrincipals(cx, fp), chars, length, filename, lineno); fp->flags = flags; cx->options = options; if (!script) return JS_FALSE; ok = js_Execute(cx, scobj, script, fp, JSFRAME_DEBUGGER | JSFRAME_EVAL, rval); js_DestroyScript(cx, script); return ok; } JS_PUBLIC_API(JSBool) JS_EvaluateInStackFrame(JSContext *cx, JSStackFrame *fp, const char *bytes, uintN length, const char *filename, uintN lineno, jsval *rval) { jschar *chars; JSBool ok; size_t len = length; chars = js_InflateString(cx, bytes, &len); if (!chars) return JS_FALSE; length = (uintN) len; ok = JS_EvaluateUCInStackFrame(cx, fp, chars, length, filename, lineno, rval); JS_free(cx, chars); return ok; } /************************************************************************/ /* XXXbe this all needs to be reworked to avoid requiring JSScope types. */ JS_PUBLIC_API(JSScopeProperty *) JS_PropertyIterator(JSObject *obj, JSScopeProperty **iteratorp) { JSScopeProperty *sprop; JSScope *scope; sprop = *iteratorp; scope = OBJ_SCOPE(obj); /* XXXbe minor(?) incompatibility: iterate in reverse definition order */ if (!sprop) { sprop = SCOPE_LAST_PROP(scope); } else { while ((sprop = sprop->parent) != NULL) { if (!SCOPE_HAD_MIDDLE_DELETE(scope)) break; if (SCOPE_HAS_PROPERTY(scope, sprop)) break; } } *iteratorp = sprop; return sprop; } JS_PUBLIC_API(JSBool) JS_GetPropertyDesc(JSContext *cx, JSObject *obj, JSScopeProperty *sprop, JSPropertyDesc *pd) { JSPropertyOp getter; JSScope *scope; JSScopeProperty *aprop; jsval lastException; JSBool wasThrowing; pd->id = ID_TO_VALUE(sprop->id); wasThrowing = cx->throwing; if (wasThrowing) { lastException = cx->exception; if (JSVAL_IS_GCTHING(lastException) && !js_AddRoot(cx, &lastException, "lastException")) { return JS_FALSE; } cx->throwing = JS_FALSE; } if (!js_GetProperty(cx, obj, sprop->id, &pd->value)) { if (!cx->throwing) { pd->flags = JSPD_ERROR; pd->value = JSVAL_VOID; } else { pd->flags = JSPD_EXCEPTION; pd->value = cx->exception; } } else { pd->flags = 0; } cx->throwing = wasThrowing; if (wasThrowing) { cx->exception = lastException; if (JSVAL_IS_GCTHING(lastException)) js_RemoveRoot(cx->runtime, &lastException); } getter = sprop->getter; pd->flags |= ((sprop->attrs & JSPROP_ENUMERATE) ? JSPD_ENUMERATE : 0) | ((sprop->attrs & JSPROP_READONLY) ? JSPD_READONLY : 0) | ((sprop->attrs & JSPROP_PERMANENT) ? JSPD_PERMANENT : 0) | ((getter == js_GetCallVariable) ? JSPD_VARIABLE : 0) | ((getter == js_GetArgument) ? JSPD_ARGUMENT : 0) | ((getter == js_GetLocalVariable) ? JSPD_VARIABLE : 0); /* for Call Object 'real' getter isn't passed in to us */ if (OBJ_GET_CLASS(cx, obj) == &js_CallClass && getter == js_CallClass.getProperty) { /* * Property of a heavyweight function's variable object having the * class-default getter. It's either an argument if permanent, or a * nested function if impermanent. Local variables have a special * getter (js_GetCallVariable, tested above) and setter, and not the * class default. */ pd->flags |= (sprop->attrs & JSPROP_PERMANENT) ? JSPD_ARGUMENT : JSPD_VARIABLE; } pd->spare = 0; pd->slot = (pd->flags & (JSPD_ARGUMENT | JSPD_VARIABLE)) ? sprop->shortid : 0; pd->alias = JSVAL_VOID; scope = OBJ_SCOPE(obj); if (SPROP_HAS_VALID_SLOT(sprop, scope)) { for (aprop = SCOPE_LAST_PROP(scope); aprop; aprop = aprop->parent) { if (aprop != sprop && aprop->slot == sprop->slot) { pd->alias = ID_TO_VALUE(aprop->id); break; } } } return JS_TRUE; } JS_PUBLIC_API(JSBool) JS_GetPropertyDescArray(JSContext *cx, JSObject *obj, JSPropertyDescArray *pda) { JSClass *clasp; JSScope *scope; uint32 i, n; JSPropertyDesc *pd; JSScopeProperty *sprop; clasp = OBJ_GET_CLASS(cx, obj); if (!OBJ_IS_NATIVE(obj) || (clasp->flags & JSCLASS_NEW_ENUMERATE)) { JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_CANT_DESCRIBE_PROPS, clasp->name); return JS_FALSE; } if (!clasp->enumerate(cx, obj)) return JS_FALSE; /* have no props, or object's scope has not mutated from that of proto */ scope = OBJ_SCOPE(obj); if (scope->object != obj || scope->entryCount == 0) { pda->length = 0; pda->array = NULL; return JS_TRUE; } n = scope->entryCount; if (n > scope->map.nslots) n = scope->map.nslots; pd = (JSPropertyDesc *) JS_malloc(cx, (size_t)n * sizeof(JSPropertyDesc)); if (!pd) return JS_FALSE; i = 0; for (sprop = SCOPE_LAST_PROP(scope); sprop; sprop = sprop->parent) { if (SCOPE_HAD_MIDDLE_DELETE(scope) && !SCOPE_HAS_PROPERTY(scope, sprop)) continue; if (!js_AddRoot(cx, &pd[i].id, NULL)) goto bad; if (!js_AddRoot(cx, &pd[i].value, NULL)) goto bad; if (!JS_GetPropertyDesc(cx, obj, sprop, &pd[i])) goto bad; if ((pd[i].flags & JSPD_ALIAS) && !js_AddRoot(cx, &pd[i].alias, NULL)) goto bad; if (++i == n) break; } pda->length = i; pda->array = pd; return JS_TRUE; bad: pda->length = i + 1; pda->array = pd; JS_PutPropertyDescArray(cx, pda); return JS_FALSE; } JS_PUBLIC_API(void) JS_PutPropertyDescArray(JSContext *cx, JSPropertyDescArray *pda) { JSPropertyDesc *pd; uint32 i; pd = pda->array; for (i = 0; i < pda->length; i++) { js_RemoveRoot(cx->runtime, &pd[i].id); js_RemoveRoot(cx->runtime, &pd[i].value); if (pd[i].flags & JSPD_ALIAS) js_RemoveRoot(cx->runtime, &pd[i].alias); } JS_free(cx, pd); } /************************************************************************/ JS_PUBLIC_API(JSBool) JS_SetDebuggerHandler(JSRuntime *rt, JSTrapHandler handler, void *closure) { rt->debuggerHandler = handler; rt->debuggerHandlerData = closure; return JS_TRUE; } JS_PUBLIC_API(JSBool) JS_SetSourceHandler(JSRuntime *rt, JSSourceHandler handler, void *closure) { rt->sourceHandler = handler; rt->sourceHandlerData = closure; return JS_TRUE; } JS_PUBLIC_API(JSBool) JS_SetExecuteHook(JSRuntime *rt, JSInterpreterHook hook, void *closure) { rt->executeHook = hook; rt->executeHookData = closure; return JS_TRUE; } JS_PUBLIC_API(JSBool) JS_SetCallHook(JSRuntime *rt, JSInterpreterHook hook, void *closure) { rt->callHook = hook; rt->callHookData = closure; return JS_TRUE; } JS_PUBLIC_API(JSBool) JS_SetObjectHook(JSRuntime *rt, JSObjectHook hook, void *closure) { rt->objectHook = hook; rt->objectHookData = closure; return JS_TRUE; } JS_PUBLIC_API(JSBool) JS_SetThrowHook(JSRuntime *rt, JSTrapHandler hook, void *closure) { rt->throwHook = hook; rt->throwHookData = closure; return JS_TRUE; } JS_PUBLIC_API(JSBool) JS_SetDebugErrorHook(JSRuntime *rt, JSDebugErrorHook hook, void *closure) { rt->debugErrorHook = hook; rt->debugErrorHookData = closure; return JS_TRUE; } /************************************************************************/ JS_PUBLIC_API(size_t) JS_GetObjectTotalSize(JSContext *cx, JSObject *obj) { size_t nbytes; JSScope *scope; nbytes = sizeof *obj + obj->map->nslots * sizeof obj->slots[0]; if (OBJ_IS_NATIVE(obj)) { scope = OBJ_SCOPE(obj); if (scope->object == obj) { nbytes += sizeof *scope; nbytes += SCOPE_CAPACITY(scope) * sizeof(JSScopeProperty *); } } return nbytes; } static size_t GetAtomTotalSize(JSContext *cx, JSAtom *atom) { size_t nbytes; nbytes = sizeof *atom; if (ATOM_IS_STRING(atom)) { nbytes += sizeof(JSString); nbytes += (ATOM_TO_STRING(atom)->length + 1) * sizeof(jschar); } else if (ATOM_IS_DOUBLE(atom)) { nbytes += sizeof(jsdouble); } else if (ATOM_IS_OBJECT(atom)) { nbytes += JS_GetObjectTotalSize(cx, ATOM_TO_OBJECT(atom)); } return nbytes; } JS_PUBLIC_API(size_t) JS_GetFunctionTotalSize(JSContext *cx, JSFunction *fun) { size_t nbytes; nbytes = sizeof *fun; if (fun->object) nbytes += JS_GetObjectTotalSize(cx, fun->object); if (FUN_INTERPRETED(fun)) nbytes += JS_GetScriptTotalSize(cx, fun->u.i.script); if (fun->atom) nbytes += GetAtomTotalSize(cx, fun->atom); return nbytes; } #include "jsemit.h" JS_PUBLIC_API(size_t) JS_GetScriptTotalSize(JSContext *cx, JSScript *script) { size_t nbytes, pbytes; JSObject *obj; jsatomid i; jssrcnote *sn, *notes; JSTryNote *tn, *tnotes; JSPrincipals *principals; nbytes = sizeof *script; obj = script->object; if (obj) nbytes += JS_GetObjectTotalSize(cx, obj); nbytes += script->length * sizeof script->code[0]; nbytes += script->atomMap.length * sizeof script->atomMap.vector[0]; for (i = 0; i < script->atomMap.length; i++) nbytes += GetAtomTotalSize(cx, script->atomMap.vector[i]); if (script->filename) nbytes += strlen(script->filename) + 1; notes = SCRIPT_NOTES(script); for (sn = notes; !SN_IS_TERMINATOR(sn); sn = SN_NEXT(sn)) continue; nbytes += (sn - notes + 1) * sizeof *sn; tnotes = script->trynotes; if (tnotes) { for (tn = tnotes; tn->catchStart; tn++) continue; nbytes += (tn - tnotes + 1) * sizeof *tn; } principals = script->principals; if (principals) { JS_ASSERT(principals->refcount); pbytes = sizeof *principals; if (principals->refcount > 1) pbytes = JS_HOWMANY(pbytes, principals->refcount); nbytes += pbytes; } return nbytes; } JS_PUBLIC_API(uint32) JS_GetTopScriptFilenameFlags(JSContext *cx, JSStackFrame *fp) { if (!fp) fp = cx->fp; while (fp) { if (fp->script) { return JS_GetScriptFilenameFlags(fp->script); } fp = fp->down; } return 0; } JS_PUBLIC_API(uint32) JS_GetScriptFilenameFlags(JSScript *script) { JS_ASSERT(script); if (!script->filename) return JSFILENAME_NULL; return js_GetScriptFilenameFlags(script->filename); } JS_PUBLIC_API(JSBool) JS_FlagScriptFilenamePrefix(JSRuntime *rt, const char *prefix, uint32 flags) { if (!js_SaveScriptFilenameRT(rt, prefix, flags)) return JS_FALSE; return JS_TRUE; } JS_PUBLIC_API(JSBool) JS_IsSystemObject(JSContext *cx, JSObject *obj) { return (*js_GetGCThingFlags(obj) & GCF_SYSTEM) != 0; } JS_PUBLIC_API(void) JS_FlagSystemObject(JSContext *cx, JSObject *obj) { uint8 *flagp; flagp = js_GetGCThingFlags(obj); *flagp |= GCF_SYSTEM; } pacparser-1.4.5/src/spidermonkey/js/src/jsdbgapi.h000066400000000000000000000343361464010763600221600ustar00rootroot00000000000000/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- * * ***** BEGIN LICENSE BLOCK ***** * Version: MPL 1.1/GPL 2.0/LGPL 2.1 * * The contents of this file are subject to the Mozilla Public License Version * 1.1 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * http://www.mozilla.org/MPL/ * * Software distributed under the License is distributed on an "AS IS" basis, * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License * for the specific language governing rights and limitations under the * License. * * The Original Code is Mozilla Communicator client code, released * March 31, 1998. * * The Initial Developer of the Original Code is * Netscape Communications Corporation. * Portions created by the Initial Developer are Copyright (C) 1998 * the Initial Developer. All Rights Reserved. * * Contributor(s): * * Alternatively, the contents of this file may be used under the terms of * either of the GNU General Public License Version 2 or later (the "GPL"), * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), * in which case the provisions of the GPL or the LGPL are applicable instead * of those above. If you wish to allow use of your version of this file only * under the terms of either the GPL or the LGPL, and not to allow others to * use your version of this file under the terms of the MPL, indicate your * decision by deleting the provisions above and replace them with the notice * and other provisions required by the GPL or the LGPL. If you do not delete * the provisions above, a recipient may use your version of this file under * the terms of any one of the MPL, the GPL or the LGPL. * * ***** END LICENSE BLOCK ***** */ #ifndef jsdbgapi_h___ #define jsdbgapi_h___ /* * JS debugger API. */ #include "jsapi.h" #include "jsopcode.h" #include "jsprvtd.h" JS_BEGIN_EXTERN_C extern void js_PatchOpcode(JSContext *cx, JSScript *script, jsbytecode *pc, JSOp op); extern JS_PUBLIC_API(JSBool) JS_SetTrap(JSContext *cx, JSScript *script, jsbytecode *pc, JSTrapHandler handler, void *closure); extern JS_PUBLIC_API(JSOp) JS_GetTrapOpcode(JSContext *cx, JSScript *script, jsbytecode *pc); extern JS_PUBLIC_API(void) JS_ClearTrap(JSContext *cx, JSScript *script, jsbytecode *pc, JSTrapHandler *handlerp, void **closurep); extern JS_PUBLIC_API(void) JS_ClearScriptTraps(JSContext *cx, JSScript *script); extern JS_PUBLIC_API(void) JS_ClearAllTraps(JSContext *cx); extern JS_PUBLIC_API(JSTrapStatus) JS_HandleTrap(JSContext *cx, JSScript *script, jsbytecode *pc, jsval *rval); extern JS_PUBLIC_API(JSBool) JS_SetInterrupt(JSRuntime *rt, JSTrapHandler handler, void *closure); extern JS_PUBLIC_API(JSBool) JS_ClearInterrupt(JSRuntime *rt, JSTrapHandler *handlerp, void **closurep); /************************************************************************/ extern JS_PUBLIC_API(JSBool) JS_SetWatchPoint(JSContext *cx, JSObject *obj, jsval id, JSWatchPointHandler handler, void *closure); extern JS_PUBLIC_API(JSBool) JS_ClearWatchPoint(JSContext *cx, JSObject *obj, jsval id, JSWatchPointHandler *handlerp, void **closurep); extern JS_PUBLIC_API(JSBool) JS_ClearWatchPointsForObject(JSContext *cx, JSObject *obj); extern JS_PUBLIC_API(JSBool) JS_ClearAllWatchPoints(JSContext *cx); #ifdef JS_HAS_OBJ_WATCHPOINT /* * Hide these non-API function prototypes by testing whether the internal * header file "jsconfig.h" has been included. */ extern void js_MarkWatchPoints(JSContext *cx); extern JSScopeProperty * js_FindWatchPoint(JSRuntime *rt, JSScope *scope, jsid id); extern JSPropertyOp js_GetWatchedSetter(JSRuntime *rt, JSScope *scope, const JSScopeProperty *sprop); extern JSBool JS_DLL_CALLBACK js_watch_set(JSContext *cx, JSObject *obj, jsval id, jsval *vp); extern JSBool JS_DLL_CALLBACK js_watch_set_wrapper(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval); extern JSPropertyOp js_WrapWatchedSetter(JSContext *cx, jsid id, uintN attrs, JSPropertyOp setter); #endif /* JS_HAS_OBJ_WATCHPOINT */ /************************************************************************/ extern JS_PUBLIC_API(uintN) JS_PCToLineNumber(JSContext *cx, JSScript *script, jsbytecode *pc); extern JS_PUBLIC_API(jsbytecode *) JS_LineNumberToPC(JSContext *cx, JSScript *script, uintN lineno); extern JS_PUBLIC_API(JSScript *) JS_GetFunctionScript(JSContext *cx, JSFunction *fun); extern JS_PUBLIC_API(JSNative) JS_GetFunctionNative(JSContext *cx, JSFunction *fun); extern JS_PUBLIC_API(JSPrincipals *) JS_GetScriptPrincipals(JSContext *cx, JSScript *script); /* * Stack Frame Iterator * * Used to iterate through the JS stack frames to extract * information from the frames. */ extern JS_PUBLIC_API(JSStackFrame *) JS_FrameIterator(JSContext *cx, JSStackFrame **iteratorp); extern JS_PUBLIC_API(JSScript *) JS_GetFrameScript(JSContext *cx, JSStackFrame *fp); extern JS_PUBLIC_API(jsbytecode *) JS_GetFramePC(JSContext *cx, JSStackFrame *fp); /* * Get the closest scripted frame below fp. If fp is null, start from cx->fp. */ extern JS_PUBLIC_API(JSStackFrame *) JS_GetScriptedCaller(JSContext *cx, JSStackFrame *fp); /* * Return a weak reference to fp's principals. A null return does not denote * an error, it means there are no principals. */ extern JS_PUBLIC_API(JSPrincipals *) JS_StackFramePrincipals(JSContext *cx, JSStackFrame *fp); /* * This API is like JS_StackFramePrincipals(cx, caller), except that if * cx->runtime->findObjectPrincipals is non-null, it returns the weaker of * the caller's principals and the object principals of fp's callee function * object (fp->argv[-2]), which is eval, Function, or a similar eval-like * method. The caller parameter should be JS_GetScriptedCaller(cx, fp). * * All eval-like methods must use JS_EvalFramePrincipals to acquire a weak * reference to the correct principals for the eval call to be secure, given * an embedding that calls JS_SetObjectPrincipalsFinder (see jsapi.h). */ extern JS_PUBLIC_API(JSPrincipals *) JS_EvalFramePrincipals(JSContext *cx, JSStackFrame *fp, JSStackFrame *caller); extern JS_PUBLIC_API(void *) JS_GetFrameAnnotation(JSContext *cx, JSStackFrame *fp); extern JS_PUBLIC_API(void) JS_SetFrameAnnotation(JSContext *cx, JSStackFrame *fp, void *annotation); extern JS_PUBLIC_API(void *) JS_GetFramePrincipalArray(JSContext *cx, JSStackFrame *fp); extern JS_PUBLIC_API(JSBool) JS_IsNativeFrame(JSContext *cx, JSStackFrame *fp); /* this is deprecated, use JS_GetFrameScopeChain instead */ extern JS_PUBLIC_API(JSObject *) JS_GetFrameObject(JSContext *cx, JSStackFrame *fp); extern JS_PUBLIC_API(JSObject *) JS_GetFrameScopeChain(JSContext *cx, JSStackFrame *fp); extern JS_PUBLIC_API(JSObject *) JS_GetFrameCallObject(JSContext *cx, JSStackFrame *fp); extern JS_PUBLIC_API(JSObject *) JS_GetFrameThis(JSContext *cx, JSStackFrame *fp); extern JS_PUBLIC_API(JSFunction *) JS_GetFrameFunction(JSContext *cx, JSStackFrame *fp); extern JS_PUBLIC_API(JSObject *) JS_GetFrameFunctionObject(JSContext *cx, JSStackFrame *fp); /* XXXrginda Initially published with typo */ #define JS_IsContructorFrame JS_IsConstructorFrame extern JS_PUBLIC_API(JSBool) JS_IsConstructorFrame(JSContext *cx, JSStackFrame *fp); extern JS_PUBLIC_API(JSBool) JS_IsDebuggerFrame(JSContext *cx, JSStackFrame *fp); extern JS_PUBLIC_API(jsval) JS_GetFrameReturnValue(JSContext *cx, JSStackFrame *fp); extern JS_PUBLIC_API(void) JS_SetFrameReturnValue(JSContext *cx, JSStackFrame *fp, jsval rval); /** * Return fp's callee function object (fp->argv[-2]) if it has one. */ extern JS_PUBLIC_API(JSObject *) JS_GetFrameCalleeObject(JSContext *cx, JSStackFrame *fp); /************************************************************************/ extern JS_PUBLIC_API(const char *) JS_GetScriptFilename(JSContext *cx, JSScript *script); extern JS_PUBLIC_API(uintN) JS_GetScriptBaseLineNumber(JSContext *cx, JSScript *script); extern JS_PUBLIC_API(uintN) JS_GetScriptLineExtent(JSContext *cx, JSScript *script); extern JS_PUBLIC_API(JSVersion) JS_GetScriptVersion(JSContext *cx, JSScript *script); /************************************************************************/ /* * Hook setters for script creation and destruction, see jsprvtd.h for the * typedefs. These macros provide binary compatibility and newer, shorter * synonyms. */ #define JS_SetNewScriptHook JS_SetNewScriptHookProc #define JS_SetDestroyScriptHook JS_SetDestroyScriptHookProc extern JS_PUBLIC_API(void) JS_SetNewScriptHook(JSRuntime *rt, JSNewScriptHook hook, void *callerdata); extern JS_PUBLIC_API(void) JS_SetDestroyScriptHook(JSRuntime *rt, JSDestroyScriptHook hook, void *callerdata); /************************************************************************/ extern JS_PUBLIC_API(JSBool) JS_EvaluateUCInStackFrame(JSContext *cx, JSStackFrame *fp, const jschar *chars, uintN length, const char *filename, uintN lineno, jsval *rval); extern JS_PUBLIC_API(JSBool) JS_EvaluateInStackFrame(JSContext *cx, JSStackFrame *fp, const char *bytes, uintN length, const char *filename, uintN lineno, jsval *rval); /************************************************************************/ typedef struct JSPropertyDesc { jsval id; /* primary id, a string or int */ jsval value; /* property value */ uint8 flags; /* flags, see below */ uint8 spare; /* unused */ uint16 slot; /* argument/variable slot */ jsval alias; /* alias id if JSPD_ALIAS flag */ } JSPropertyDesc; #define JSPD_ENUMERATE 0x01 /* visible to for/in loop */ #define JSPD_READONLY 0x02 /* assignment is error */ #define JSPD_PERMANENT 0x04 /* property cannot be deleted */ #define JSPD_ALIAS 0x08 /* property has an alias id */ #define JSPD_ARGUMENT 0x10 /* argument to function */ #define JSPD_VARIABLE 0x20 /* local variable in function */ #define JSPD_EXCEPTION 0x40 /* exception occurred fetching the property, */ /* value is exception */ #define JSPD_ERROR 0x80 /* native getter returned JS_FALSE without */ /* throwing an exception */ typedef struct JSPropertyDescArray { uint32 length; /* number of elements in array */ JSPropertyDesc *array; /* alloc'd by Get, freed by Put */ } JSPropertyDescArray; extern JS_PUBLIC_API(JSScopeProperty *) JS_PropertyIterator(JSObject *obj, JSScopeProperty **iteratorp); extern JS_PUBLIC_API(JSBool) JS_GetPropertyDesc(JSContext *cx, JSObject *obj, JSScopeProperty *sprop, JSPropertyDesc *pd); extern JS_PUBLIC_API(JSBool) JS_GetPropertyDescArray(JSContext *cx, JSObject *obj, JSPropertyDescArray *pda); extern JS_PUBLIC_API(void) JS_PutPropertyDescArray(JSContext *cx, JSPropertyDescArray *pda); /************************************************************************/ extern JS_PUBLIC_API(JSBool) JS_SetDebuggerHandler(JSRuntime *rt, JSTrapHandler handler, void *closure); extern JS_PUBLIC_API(JSBool) JS_SetSourceHandler(JSRuntime *rt, JSSourceHandler handler, void *closure); extern JS_PUBLIC_API(JSBool) JS_SetExecuteHook(JSRuntime *rt, JSInterpreterHook hook, void *closure); extern JS_PUBLIC_API(JSBool) JS_SetCallHook(JSRuntime *rt, JSInterpreterHook hook, void *closure); extern JS_PUBLIC_API(JSBool) JS_SetObjectHook(JSRuntime *rt, JSObjectHook hook, void *closure); extern JS_PUBLIC_API(JSBool) JS_SetThrowHook(JSRuntime *rt, JSTrapHandler hook, void *closure); extern JS_PUBLIC_API(JSBool) JS_SetDebugErrorHook(JSRuntime *rt, JSDebugErrorHook hook, void *closure); /************************************************************************/ extern JS_PUBLIC_API(size_t) JS_GetObjectTotalSize(JSContext *cx, JSObject *obj); extern JS_PUBLIC_API(size_t) JS_GetFunctionTotalSize(JSContext *cx, JSFunction *fun); extern JS_PUBLIC_API(size_t) JS_GetScriptTotalSize(JSContext *cx, JSScript *script); /* * Get the top-most running script on cx starting from fp, or from the top of * cx's frame stack if fp is null, and return its script filename flags. If * the script has a null filename member, return JSFILENAME_NULL. */ extern JS_PUBLIC_API(uint32) JS_GetTopScriptFilenameFlags(JSContext *cx, JSStackFrame *fp); /* * Get the script filename flags for the script. If the script doesn't have a * filename, return JSFILENAME_NULL. */ extern JS_PUBLIC_API(uint32) JS_GetScriptFilenameFlags(JSScript *script); /* * Associate flags with a script filename prefix in rt, so that any subsequent * script compilation will inherit those flags if the script's filename is the * same as prefix, or if prefix is a substring of the script's filename. * * The API defines only one flag bit, JSFILENAME_SYSTEM, leaving the remaining * 31 bits up to the API client to define. The union of all 32 bits must not * be a legal combination, however, in order to preserve JSFILENAME_NULL as a * unique value. API clients may depend on JSFILENAME_SYSTEM being a set bit * in JSFILENAME_NULL -- a script with a null filename member is presumed to * be a "system" script. */ extern JS_PUBLIC_API(JSBool) JS_FlagScriptFilenamePrefix(JSRuntime *rt, const char *prefix, uint32 flags); #define JSFILENAME_NULL 0xffffffff /* null script filename */ #define JSFILENAME_SYSTEM 0x00000001 /* "system" script, see below */ /* * Return true if obj is a "system" object, that is, one flagged by a prior * call to JS_FlagSystemObject(cx, obj). What "system" means is up to the API * client, but it can be used to coordinate access control policies based on * script filenames and their prefixes, using JS_FlagScriptFilenamePrefix and * JS_GetTopScriptFilenameFlags. */ extern JS_PUBLIC_API(JSBool) JS_IsSystemObject(JSContext *cx, JSObject *obj); /* * Flag obj as a "system" object. The API client can flag system objects to * optimize access control checks. The engine stores but does not interpret * the per-object flag set by this call. */ extern JS_PUBLIC_API(void) JS_FlagSystemObject(JSContext *cx, JSObject *obj); JS_END_EXTERN_C #endif /* jsdbgapi_h___ */ pacparser-1.4.5/src/spidermonkey/js/src/jsdhash.c000066400000000000000000000651501464010763600220120ustar00rootroot00000000000000/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ /* ***** BEGIN LICENSE BLOCK ***** * Version: MPL 1.1/GPL 2.0/LGPL 2.1 * * The contents of this file are subject to the Mozilla Public License Version * 1.1 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * http://www.mozilla.org/MPL/ * * Software distributed under the License is distributed on an "AS IS" basis, * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License * for the specific language governing rights and limitations under the * License. * * The Original Code is Mozilla JavaScript code. * * The Initial Developer of the Original Code is * Netscape Communications Corporation. * Portions created by the Initial Developer are Copyright (C) 1999-2001 * the Initial Developer. All Rights Reserved. * * Contributor(s): * Brendan Eich (Original Author) * Chris Waterson * * Alternatively, the contents of this file may be used under the terms of * either of the GNU General Public License Version 2 or later (the "GPL"), * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), * in which case the provisions of the GPL or the LGPL are applicable instead * of those above. If you wish to allow use of your version of this file only * under the terms of either the GPL or the LGPL, and not to allow others to * use your version of this file under the terms of the MPL, indicate your * decision by deleting the provisions above and replace them with the notice * and other provisions required by the GPL or the LGPL. If you do not delete * the provisions above, a recipient may use your version of this file under * the terms of any one of the MPL, the GPL or the LGPL. * * ***** END LICENSE BLOCK ***** */ /* * Double hashing implementation. */ #include #include #include #include "jsbit.h" #include "jsdhash.h" #include "jsutil.h" /* for JS_ASSERT */ #ifdef JS_DHASHMETER # if defined MOZILLA_CLIENT && defined DEBUG_XXXbrendan # include "nsTraceMalloc.h" # endif # define METER(x) x #else # define METER(x) /* nothing */ #endif /* * The following DEBUG-only code is used to assert that calls to one of * table->ops or to an enumerator do not cause re-entry into a call that * can mutate the table. The recursion level is stored in additional * space allocated at the end of the entry store to avoid changing * JSDHashTable, which could cause issues when mixing DEBUG and * non-DEBUG components. */ #ifdef DEBUG #define RECURSION_LEVEL(table_) (*(uint32*)(table_->entryStore + \ JS_DHASH_TABLE_SIZE(table_) * \ table_->entrySize)) #define ENTRY_STORE_EXTRA sizeof(uint32) #define INCREMENT_RECURSION_LEVEL(table_) (++RECURSION_LEVEL(table_)) #define DECREMENT_RECURSION_LEVEL(table_) (--RECURSION_LEVEL(table_)) #else #define ENTRY_STORE_EXTRA 0 #define INCREMENT_RECURSION_LEVEL(table_) ((void)1) #define DECREMENT_RECURSION_LEVEL(table_) ((void)0) #endif /* defined(DEBUG) */ JS_PUBLIC_API(void *) JS_DHashAllocTable(JSDHashTable *table, uint32 nbytes) { return malloc(nbytes); } JS_PUBLIC_API(void) JS_DHashFreeTable(JSDHashTable *table, void *ptr) { free(ptr); } JS_PUBLIC_API(JSDHashNumber) JS_DHashStringKey(JSDHashTable *table, const void *key) { JSDHashNumber h; const unsigned char *s; h = 0; for (s = key; *s != '\0'; s++) h = (h >> (JS_DHASH_BITS - 4)) ^ (h << 4) ^ *s; return h; } JS_PUBLIC_API(const void *) JS_DHashGetKeyStub(JSDHashTable *table, JSDHashEntryHdr *entry) { JSDHashEntryStub *stub = (JSDHashEntryStub *)entry; return stub->key; } JS_PUBLIC_API(JSDHashNumber) JS_DHashVoidPtrKeyStub(JSDHashTable *table, const void *key) { return (JSDHashNumber)(unsigned long)key >> 2; } JS_PUBLIC_API(JSBool) JS_DHashMatchEntryStub(JSDHashTable *table, const JSDHashEntryHdr *entry, const void *key) { const JSDHashEntryStub *stub = (const JSDHashEntryStub *)entry; return stub->key == key; } JS_PUBLIC_API(JSBool) JS_DHashMatchStringKey(JSDHashTable *table, const JSDHashEntryHdr *entry, const void *key) { const JSDHashEntryStub *stub = (const JSDHashEntryStub *)entry; /* XXX tolerate null keys on account of sloppy Mozilla callers. */ return stub->key == key || (stub->key && key && strcmp(stub->key, key) == 0); } JS_PUBLIC_API(void) JS_DHashMoveEntryStub(JSDHashTable *table, const JSDHashEntryHdr *from, JSDHashEntryHdr *to) { memcpy(to, from, table->entrySize); } JS_PUBLIC_API(void) JS_DHashClearEntryStub(JSDHashTable *table, JSDHashEntryHdr *entry) { memset(entry, 0, table->entrySize); } JS_PUBLIC_API(void) JS_DHashFreeStringKey(JSDHashTable *table, JSDHashEntryHdr *entry) { const JSDHashEntryStub *stub = (const JSDHashEntryStub *)entry; free((void *) stub->key); memset(entry, 0, table->entrySize); } JS_PUBLIC_API(void) JS_DHashFinalizeStub(JSDHashTable *table) { } static const JSDHashTableOps stub_ops = { JS_DHashAllocTable, JS_DHashFreeTable, JS_DHashGetKeyStub, JS_DHashVoidPtrKeyStub, JS_DHashMatchEntryStub, JS_DHashMoveEntryStub, JS_DHashClearEntryStub, JS_DHashFinalizeStub, NULL }; JS_PUBLIC_API(const JSDHashTableOps *) JS_DHashGetStubOps(void) { return &stub_ops; } JS_PUBLIC_API(JSDHashTable *) JS_NewDHashTable(const JSDHashTableOps *ops, void *data, uint32 entrySize, uint32 capacity) { JSDHashTable *table; table = (JSDHashTable *) malloc(sizeof *table); if (!table) return NULL; if (!JS_DHashTableInit(table, ops, data, entrySize, capacity)) { free(table); return NULL; } return table; } JS_PUBLIC_API(void) JS_DHashTableDestroy(JSDHashTable *table) { JS_DHashTableFinish(table); free(table); } JS_PUBLIC_API(JSBool) JS_DHashTableInit(JSDHashTable *table, const JSDHashTableOps *ops, void *data, uint32 entrySize, uint32 capacity) { int log2; uint32 nbytes; #ifdef DEBUG if (entrySize > 10 * sizeof(void *)) { fprintf(stderr, "jsdhash: for the table at address %p, the given entrySize" " of %lu %s favors chaining over double hashing.\n", (void *)table, (unsigned long) entrySize, (entrySize > 16 * sizeof(void*)) ? "definitely" : "probably"); } #endif table->ops = ops; table->data = data; if (capacity < JS_DHASH_MIN_SIZE) capacity = JS_DHASH_MIN_SIZE; JS_CEILING_LOG2(log2, capacity); capacity = JS_BIT(log2); if (capacity >= JS_DHASH_SIZE_LIMIT) return JS_FALSE; table->hashShift = JS_DHASH_BITS - log2; table->maxAlphaFrac = 0xC0; /* .75 */ table->minAlphaFrac = 0x40; /* .25 */ table->entrySize = entrySize; table->entryCount = table->removedCount = 0; table->generation = 0; nbytes = capacity * entrySize; table->entryStore = ops->allocTable(table, nbytes + ENTRY_STORE_EXTRA); if (!table->entryStore) return JS_FALSE; memset(table->entryStore, 0, nbytes); METER(memset(&table->stats, 0, sizeof table->stats)); #ifdef DEBUG RECURSION_LEVEL(table) = 0; #endif return JS_TRUE; } /* * Compute max and min load numbers (entry counts) from table params. */ #define MAX_LOAD(table, size) (((table)->maxAlphaFrac * (size)) >> 8) #define MIN_LOAD(table, size) (((table)->minAlphaFrac * (size)) >> 8) JS_PUBLIC_API(void) JS_DHashTableSetAlphaBounds(JSDHashTable *table, float maxAlpha, float minAlpha) { uint32 size; /* * Reject obviously insane bounds, rather than trying to guess what the * buggy caller intended. */ JS_ASSERT(0.5 <= maxAlpha && maxAlpha < 1 && 0 <= minAlpha); if (maxAlpha < 0.5 || 1 <= maxAlpha || minAlpha < 0) return; /* * Ensure that at least one entry will always be free. If maxAlpha at * minimum size leaves no entries free, reduce maxAlpha based on minimum * size and the precision limit of maxAlphaFrac's fixed point format. */ JS_ASSERT(JS_DHASH_MIN_SIZE - (maxAlpha * JS_DHASH_MIN_SIZE) >= 1); if (JS_DHASH_MIN_SIZE - (maxAlpha * JS_DHASH_MIN_SIZE) < 1) { maxAlpha = (float) (JS_DHASH_MIN_SIZE - JS_MAX(JS_DHASH_MIN_SIZE / 256, 1)) / JS_DHASH_MIN_SIZE; } /* * Ensure that minAlpha is strictly less than half maxAlpha. Take care * not to truncate an entry's worth of alpha when storing in minAlphaFrac * (8-bit fixed point format). */ JS_ASSERT(minAlpha < maxAlpha / 2); if (minAlpha >= maxAlpha / 2) { size = JS_DHASH_TABLE_SIZE(table); minAlpha = (size * maxAlpha - JS_MAX(size / 256, 1)) / (2 * size); } table->maxAlphaFrac = (uint8)(maxAlpha * 256); table->minAlphaFrac = (uint8)(minAlpha * 256); } /* * Double hashing needs the second hash code to be relatively prime to table * size, so we simply make hash2 odd. */ #define HASH1(hash0, shift) ((hash0) >> (shift)) #define HASH2(hash0,log2,shift) ((((hash0) << (log2)) >> (shift)) | 1) /* * Reserve keyHash 0 for free entries and 1 for removed-entry sentinels. Note * that a removed-entry sentinel need be stored only if the removed entry had * a colliding entry added after it. Therefore we can use 1 as the collision * flag in addition to the removed-entry sentinel value. Multiplicative hash * uses the high order bits of keyHash, so this least-significant reservation * should not hurt the hash function's effectiveness much. * * If you change any of these magic numbers, also update JS_DHASH_ENTRY_IS_LIVE * in jsdhash.h. It used to be private to jsdhash.c, but then became public to * assist iterator writers who inspect table->entryStore directly. */ #define COLLISION_FLAG ((JSDHashNumber) 1) #define MARK_ENTRY_FREE(entry) ((entry)->keyHash = 0) #define MARK_ENTRY_REMOVED(entry) ((entry)->keyHash = 1) #define ENTRY_IS_REMOVED(entry) ((entry)->keyHash == 1) #define ENTRY_IS_LIVE(entry) JS_DHASH_ENTRY_IS_LIVE(entry) #define ENSURE_LIVE_KEYHASH(hash0) if (hash0 < 2) hash0 -= 2; else (void)0 /* Match an entry's keyHash against an unstored one computed from a key. */ #define MATCH_ENTRY_KEYHASH(entry,hash0) \ (((entry)->keyHash & ~COLLISION_FLAG) == (hash0)) /* Compute the address of the indexed entry in table. */ #define ADDRESS_ENTRY(table, index) \ ((JSDHashEntryHdr *)((table)->entryStore + (index) * (table)->entrySize)) JS_PUBLIC_API(void) JS_DHashTableFinish(JSDHashTable *table) { char *entryAddr, *entryLimit; uint32 entrySize; JSDHashEntryHdr *entry; #ifdef DEBUG_XXXbrendan static FILE *dumpfp = NULL; if (!dumpfp) dumpfp = fopen("/tmp/jsdhash.bigdump", "w"); if (dumpfp) { #ifdef MOZILLA_CLIENT NS_TraceStack(1, dumpfp); #endif JS_DHashTableDumpMeter(table, NULL, dumpfp); fputc('\n', dumpfp); } #endif INCREMENT_RECURSION_LEVEL(table); /* Call finalize before clearing entries, so it can enumerate them. */ table->ops->finalize(table); /* Clear any remaining live entries. */ entryAddr = table->entryStore; entrySize = table->entrySize; entryLimit = entryAddr + JS_DHASH_TABLE_SIZE(table) * entrySize; while (entryAddr < entryLimit) { entry = (JSDHashEntryHdr *)entryAddr; if (ENTRY_IS_LIVE(entry)) { METER(table->stats.removeEnums++); table->ops->clearEntry(table, entry); } entryAddr += entrySize; } DECREMENT_RECURSION_LEVEL(table); JS_ASSERT(RECURSION_LEVEL(table) == 0); /* Free entry storage last. */ table->ops->freeTable(table, table->entryStore); } static JSDHashEntryHdr * JS_DHASH_FASTCALL SearchTable(JSDHashTable *table, const void *key, JSDHashNumber keyHash, JSDHashOperator op) { JSDHashNumber hash1, hash2; int hashShift, sizeLog2; JSDHashEntryHdr *entry, *firstRemoved; JSDHashMatchEntry matchEntry; uint32 sizeMask; METER(table->stats.searches++); JS_ASSERT(!(keyHash & COLLISION_FLAG)); /* Compute the primary hash address. */ hashShift = table->hashShift; hash1 = HASH1(keyHash, hashShift); entry = ADDRESS_ENTRY(table, hash1); /* Miss: return space for a new entry. */ if (JS_DHASH_ENTRY_IS_FREE(entry)) { METER(table->stats.misses++); return entry; } /* Hit: return entry. */ matchEntry = table->ops->matchEntry; if (MATCH_ENTRY_KEYHASH(entry, keyHash) && matchEntry(table, entry, key)) { METER(table->stats.hits++); return entry; } /* Collision: double hash. */ sizeLog2 = JS_DHASH_BITS - table->hashShift; hash2 = HASH2(keyHash, sizeLog2, hashShift); sizeMask = JS_BITMASK(sizeLog2); /* Save the first removed entry pointer so JS_DHASH_ADD can recycle it. */ if (ENTRY_IS_REMOVED(entry)) { firstRemoved = entry; } else { firstRemoved = NULL; if (op == JS_DHASH_ADD) entry->keyHash |= COLLISION_FLAG; } for (;;) { METER(table->stats.steps++); hash1 -= hash2; hash1 &= sizeMask; entry = ADDRESS_ENTRY(table, hash1); if (JS_DHASH_ENTRY_IS_FREE(entry)) { METER(table->stats.misses++); return (firstRemoved && op == JS_DHASH_ADD) ? firstRemoved : entry; } if (MATCH_ENTRY_KEYHASH(entry, keyHash) && matchEntry(table, entry, key)) { METER(table->stats.hits++); return entry; } if (ENTRY_IS_REMOVED(entry)) { if (!firstRemoved) firstRemoved = entry; } else { if (op == JS_DHASH_ADD) entry->keyHash |= COLLISION_FLAG; } } /* NOTREACHED */ return NULL; } static JSBool ChangeTable(JSDHashTable *table, int deltaLog2) { int oldLog2, newLog2; uint32 oldCapacity, newCapacity; char *newEntryStore, *oldEntryStore, *oldEntryAddr; uint32 entrySize, i, nbytes; JSDHashEntryHdr *oldEntry, *newEntry; JSDHashGetKey getKey; JSDHashMoveEntry moveEntry; #ifdef DEBUG uint32 recursionLevel; #endif /* Look, but don't touch, until we succeed in getting new entry store. */ oldLog2 = JS_DHASH_BITS - table->hashShift; newLog2 = oldLog2 + deltaLog2; oldCapacity = JS_BIT(oldLog2); newCapacity = JS_BIT(newLog2); if (newCapacity >= JS_DHASH_SIZE_LIMIT) return JS_FALSE; entrySize = table->entrySize; nbytes = newCapacity * entrySize; newEntryStore = table->ops->allocTable(table, nbytes + ENTRY_STORE_EXTRA); if (!newEntryStore) return JS_FALSE; /* We can't fail from here on, so update table parameters. */ #ifdef DEBUG recursionLevel = RECURSION_LEVEL(table); #endif table->hashShift = JS_DHASH_BITS - newLog2; table->removedCount = 0; table->generation++; /* Assign the new entry store to table. */ memset(newEntryStore, 0, nbytes); oldEntryAddr = oldEntryStore = table->entryStore; table->entryStore = newEntryStore; getKey = table->ops->getKey; moveEntry = table->ops->moveEntry; #ifdef DEBUG RECURSION_LEVEL(table) = recursionLevel; #endif /* Copy only live entries, leaving removed ones behind. */ for (i = 0; i < oldCapacity; i++) { oldEntry = (JSDHashEntryHdr *)oldEntryAddr; if (ENTRY_IS_LIVE(oldEntry)) { oldEntry->keyHash &= ~COLLISION_FLAG; newEntry = SearchTable(table, getKey(table, oldEntry), oldEntry->keyHash, JS_DHASH_ADD); JS_ASSERT(JS_DHASH_ENTRY_IS_FREE(newEntry)); moveEntry(table, oldEntry, newEntry); newEntry->keyHash = oldEntry->keyHash; } oldEntryAddr += entrySize; } table->ops->freeTable(table, oldEntryStore); return JS_TRUE; } JS_PUBLIC_API(JSDHashEntryHdr *) JS_DHASH_FASTCALL JS_DHashTableOperate(JSDHashTable *table, const void *key, JSDHashOperator op) { JSDHashNumber keyHash; JSDHashEntryHdr *entry; uint32 size; int deltaLog2; JS_ASSERT(op == JS_DHASH_LOOKUP || RECURSION_LEVEL(table) == 0); INCREMENT_RECURSION_LEVEL(table); keyHash = table->ops->hashKey(table, key); keyHash *= JS_DHASH_GOLDEN_RATIO; /* Avoid 0 and 1 hash codes, they indicate free and removed entries. */ ENSURE_LIVE_KEYHASH(keyHash); keyHash &= ~COLLISION_FLAG; switch (op) { case JS_DHASH_LOOKUP: METER(table->stats.lookups++); entry = SearchTable(table, key, keyHash, op); break; case JS_DHASH_ADD: /* * If alpha is >= .75, grow or compress the table. If key is already * in the table, we may grow once more than necessary, but only if we * are on the edge of being overloaded. */ size = JS_DHASH_TABLE_SIZE(table); if (table->entryCount + table->removedCount >= MAX_LOAD(table, size)) { /* Compress if a quarter or more of all entries are removed. */ if (table->removedCount >= size >> 2) { METER(table->stats.compresses++); deltaLog2 = 0; } else { METER(table->stats.grows++); deltaLog2 = 1; } /* * Grow or compress table, returning null if ChangeTable fails and * falling through might claim the last free entry. */ if (!ChangeTable(table, deltaLog2) && table->entryCount + table->removedCount == size - 1) { METER(table->stats.addFailures++); entry = NULL; break; } } /* * Look for entry after possibly growing, so we don't have to add it, * then skip it while growing the table and re-add it after. */ entry = SearchTable(table, key, keyHash, op); if (!ENTRY_IS_LIVE(entry)) { /* Initialize the entry, indicating that it's no longer free. */ METER(table->stats.addMisses++); if (ENTRY_IS_REMOVED(entry)) { METER(table->stats.addOverRemoved++); table->removedCount--; keyHash |= COLLISION_FLAG; } if (table->ops->initEntry && !table->ops->initEntry(table, entry, key)) { /* We haven't claimed entry yet; fail with null return. */ memset(entry + 1, 0, table->entrySize - sizeof *entry); entry = NULL; break; } entry->keyHash = keyHash; table->entryCount++; } METER(else table->stats.addHits++); break; case JS_DHASH_REMOVE: entry = SearchTable(table, key, keyHash, op); if (ENTRY_IS_LIVE(entry)) { /* Clear this entry and mark it as "removed". */ METER(table->stats.removeHits++); JS_DHashTableRawRemove(table, entry); /* Shrink if alpha is <= .25 and table isn't too small already. */ size = JS_DHASH_TABLE_SIZE(table); if (size > JS_DHASH_MIN_SIZE && table->entryCount <= MIN_LOAD(table, size)) { METER(table->stats.shrinks++); (void) ChangeTable(table, -1); } } METER(else table->stats.removeMisses++); entry = NULL; break; default: JS_ASSERT(0); entry = NULL; } DECREMENT_RECURSION_LEVEL(table); return entry; } JS_PUBLIC_API(void) JS_DHashTableRawRemove(JSDHashTable *table, JSDHashEntryHdr *entry) { JSDHashNumber keyHash; /* load first in case clearEntry goofs it */ JS_ASSERT(JS_DHASH_ENTRY_IS_LIVE(entry)); keyHash = entry->keyHash; table->ops->clearEntry(table, entry); if (keyHash & COLLISION_FLAG) { MARK_ENTRY_REMOVED(entry); table->removedCount++; } else { METER(table->stats.removeFrees++); MARK_ENTRY_FREE(entry); } table->entryCount--; } JS_PUBLIC_API(uint32) JS_DHashTableEnumerate(JSDHashTable *table, JSDHashEnumerator etor, void *arg) { char *entryAddr, *entryLimit; uint32 i, capacity, entrySize, ceiling; JSBool didRemove; JSDHashEntryHdr *entry; JSDHashOperator op; INCREMENT_RECURSION_LEVEL(table); entryAddr = table->entryStore; entrySize = table->entrySize; capacity = JS_DHASH_TABLE_SIZE(table); entryLimit = entryAddr + capacity * entrySize; i = 0; didRemove = JS_FALSE; while (entryAddr < entryLimit) { entry = (JSDHashEntryHdr *)entryAddr; if (ENTRY_IS_LIVE(entry)) { op = etor(table, entry, i++, arg); if (op & JS_DHASH_REMOVE) { METER(table->stats.removeEnums++); JS_DHashTableRawRemove(table, entry); didRemove = JS_TRUE; } if (op & JS_DHASH_STOP) break; } entryAddr += entrySize; } JS_ASSERT(!didRemove || RECURSION_LEVEL(table) == 1); /* * Shrink or compress if a quarter or more of all entries are removed, or * if the table is underloaded according to the configured minimum alpha, * and is not minimal-size already. Do this only if we removed above, so * non-removing enumerations can count on stable table->entryStore until * the next non-lookup-Operate or removing-Enumerate. */ if (didRemove && (table->removedCount >= capacity >> 2 || (capacity > JS_DHASH_MIN_SIZE && table->entryCount <= MIN_LOAD(table, capacity)))) { METER(table->stats.enumShrinks++); capacity = table->entryCount; capacity += capacity >> 1; if (capacity < JS_DHASH_MIN_SIZE) capacity = JS_DHASH_MIN_SIZE; JS_CEILING_LOG2(ceiling, capacity); ceiling -= JS_DHASH_BITS - table->hashShift; (void) ChangeTable(table, ceiling); } DECREMENT_RECURSION_LEVEL(table); return i; } #ifdef JS_DHASHMETER #include JS_PUBLIC_API(void) JS_DHashTableDumpMeter(JSDHashTable *table, JSDHashEnumerator dump, FILE *fp) { char *entryAddr; uint32 entrySize, entryCount; int hashShift, sizeLog2; uint32 i, tableSize, sizeMask, chainLen, maxChainLen, chainCount; JSDHashNumber hash1, hash2, saveHash1, maxChainHash1, maxChainHash2; double sqsum, mean, variance, sigma; JSDHashEntryHdr *entry, *probe; entryAddr = table->entryStore; entrySize = table->entrySize; hashShift = table->hashShift; sizeLog2 = JS_DHASH_BITS - hashShift; tableSize = JS_DHASH_TABLE_SIZE(table); sizeMask = JS_BITMASK(sizeLog2); chainCount = maxChainLen = 0; hash2 = 0; sqsum = 0; for (i = 0; i < tableSize; i++) { entry = (JSDHashEntryHdr *)entryAddr; entryAddr += entrySize; if (!ENTRY_IS_LIVE(entry)) continue; hash1 = HASH1(entry->keyHash & ~COLLISION_FLAG, hashShift); saveHash1 = hash1; probe = ADDRESS_ENTRY(table, hash1); chainLen = 1; if (probe == entry) { /* Start of a (possibly unit-length) chain. */ chainCount++; } else { hash2 = HASH2(entry->keyHash & ~COLLISION_FLAG, sizeLog2, hashShift); do { chainLen++; hash1 -= hash2; hash1 &= sizeMask; probe = ADDRESS_ENTRY(table, hash1); } while (probe != entry); } sqsum += chainLen * chainLen; if (chainLen > maxChainLen) { maxChainLen = chainLen; maxChainHash1 = saveHash1; maxChainHash2 = hash2; } } entryCount = table->entryCount; if (entryCount && chainCount) { mean = (double)entryCount / chainCount; variance = chainCount * sqsum - entryCount * entryCount; if (variance < 0 || chainCount == 1) variance = 0; else variance /= chainCount * (chainCount - 1); sigma = sqrt(variance); } else { mean = sigma = 0; } fprintf(fp, "Double hashing statistics:\n"); fprintf(fp, " table size (in entries): %u\n", tableSize); fprintf(fp, " number of entries: %u\n", table->entryCount); fprintf(fp, " number of removed entries: %u\n", table->removedCount); fprintf(fp, " number of searches: %u\n", table->stats.searches); fprintf(fp, " number of hits: %u\n", table->stats.hits); fprintf(fp, " number of misses: %u\n", table->stats.misses); fprintf(fp, " mean steps per search: %g\n", table->stats.searches ? (double)table->stats.steps / table->stats.searches : 0.); fprintf(fp, " mean hash chain length: %g\n", mean); fprintf(fp, " standard deviation: %g\n", sigma); fprintf(fp, " maximum hash chain length: %u\n", maxChainLen); fprintf(fp, " number of lookups: %u\n", table->stats.lookups); fprintf(fp, " adds that made a new entry: %u\n", table->stats.addMisses); fprintf(fp, "adds that recycled removeds: %u\n", table->stats.addOverRemoved); fprintf(fp, " adds that found an entry: %u\n", table->stats.addHits); fprintf(fp, " add failures: %u\n", table->stats.addFailures); fprintf(fp, " useful removes: %u\n", table->stats.removeHits); fprintf(fp, " useless removes: %u\n", table->stats.removeMisses); fprintf(fp, "removes that freed an entry: %u\n", table->stats.removeFrees); fprintf(fp, " removes while enumerating: %u\n", table->stats.removeEnums); fprintf(fp, " number of grows: %u\n", table->stats.grows); fprintf(fp, " number of shrinks: %u\n", table->stats.shrinks); fprintf(fp, " number of compresses: %u\n", table->stats.compresses); fprintf(fp, "number of enumerate shrinks: %u\n", table->stats.enumShrinks); if (dump && maxChainLen && hash2) { fputs("Maximum hash chain:\n", fp); hash1 = maxChainHash1; hash2 = maxChainHash2; entry = ADDRESS_ENTRY(table, hash1); i = 0; do { if (dump(table, entry, i++, fp) != JS_DHASH_NEXT) break; hash1 -= hash2; hash1 &= sizeMask; entry = ADDRESS_ENTRY(table, hash1); } while (JS_DHASH_ENTRY_IS_BUSY(entry)); } } #endif /* JS_DHASHMETER */ pacparser-1.4.5/src/spidermonkey/js/src/jsdhash.h000066400000000000000000000606341464010763600220210ustar00rootroot00000000000000/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ /* ***** BEGIN LICENSE BLOCK ***** * Version: MPL 1.1/GPL 2.0/LGPL 2.1 * * The contents of this file are subject to the Mozilla Public License Version * 1.1 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * http://www.mozilla.org/MPL/ * * Software distributed under the License is distributed on an "AS IS" basis, * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License * for the specific language governing rights and limitations under the * License. * * The Original Code is Mozilla JavaScript code. * * The Initial Developer of the Original Code is * Netscape Communications Corporation. * Portions created by the Initial Developer are Copyright (C) 1999-2001 * the Initial Developer. All Rights Reserved. * * Contributor(s): * Brendan Eich (Original Author) * * Alternatively, the contents of this file may be used under the terms of * either of the GNU General Public License Version 2 or later (the "GPL"), * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), * in which case the provisions of the GPL or the LGPL are applicable instead * of those above. If you wish to allow use of your version of this file only * under the terms of either the GPL or the LGPL, and not to allow others to * use your version of this file under the terms of the MPL, indicate your * decision by deleting the provisions above and replace them with the notice * and other provisions required by the GPL or the LGPL. If you do not delete * the provisions above, a recipient may use your version of this file under * the terms of any one of the MPL, the GPL or the LGPL. * * ***** END LICENSE BLOCK ***** */ #ifndef jsdhash_h___ #define jsdhash_h___ /* * Double hashing, a la Knuth 6. */ #include "jstypes.h" JS_BEGIN_EXTERN_C #if defined(__GNUC__) && defined(__i386__) && (__GNUC__ >= 3) && !defined(XP_OS2) #define JS_DHASH_FASTCALL __attribute__ ((regparm (3),stdcall)) #elif defined(XP_WIN) #define JS_DHASH_FASTCALL __fastcall #else #define JS_DHASH_FASTCALL #endif #ifdef DEBUG_XXXbrendan #define JS_DHASHMETER 1 #endif /* Table size limit, do not equal or exceed (see min&maxAlphaFrac, below). */ #undef JS_DHASH_SIZE_LIMIT #define JS_DHASH_SIZE_LIMIT JS_BIT(24) /* Minimum table size, or gross entry count (net is at most .75 loaded). */ #ifndef JS_DHASH_MIN_SIZE #define JS_DHASH_MIN_SIZE 16 #elif (JS_DHASH_MIN_SIZE & (JS_DHASH_MIN_SIZE - 1)) != 0 #error "JS_DHASH_MIN_SIZE must be a power of two!" #endif /* * Multiplicative hash uses an unsigned 32 bit integer and the golden ratio, * expressed as a fixed-point 32-bit fraction. */ #define JS_DHASH_BITS 32 #define JS_DHASH_GOLDEN_RATIO 0x9E3779B9U /* Primitive and forward-struct typedefs. */ typedef uint32 JSDHashNumber; typedef struct JSDHashEntryHdr JSDHashEntryHdr; typedef struct JSDHashEntryStub JSDHashEntryStub; typedef struct JSDHashTable JSDHashTable; typedef struct JSDHashTableOps JSDHashTableOps; /* * Table entry header structure. * * In order to allow in-line allocation of key and value, we do not declare * either here. Instead, the API uses const void *key as a formal parameter, * and asks each entry for its key when necessary via a getKey callback, used * when growing or shrinking the table. Other callback types are defined * below and grouped into the JSDHashTableOps structure, for single static * initialization per hash table sub-type. * * Each hash table sub-type should nest the JSDHashEntryHdr structure at the * front of its particular entry type. The keyHash member contains the result * of multiplying the hash code returned from the hashKey callback (see below) * by JS_DHASH_GOLDEN_RATIO, then constraining the result to avoid the magic 0 * and 1 values. The stored keyHash value is table size invariant, and it is * maintained automatically by JS_DHashTableOperate -- users should never set * it, and its only uses should be via the entry macros below. * * The JS_DHASH_ENTRY_IS_LIVE macro tests whether entry is neither free nor * removed. An entry may be either busy or free; if busy, it may be live or * removed. Consumers of this API should not access members of entries that * are not live. * * However, use JS_DHASH_ENTRY_IS_BUSY for faster liveness testing of entries * returned by JS_DHashTableOperate, as JS_DHashTableOperate never returns a * non-live, busy (i.e., removed) entry pointer to its caller. See below for * more details on JS_DHashTableOperate's calling rules. */ struct JSDHashEntryHdr { JSDHashNumber keyHash; /* every entry must begin like this */ }; #define JS_DHASH_ENTRY_IS_FREE(entry) ((entry)->keyHash == 0) #define JS_DHASH_ENTRY_IS_BUSY(entry) (!JS_DHASH_ENTRY_IS_FREE(entry)) #define JS_DHASH_ENTRY_IS_LIVE(entry) ((entry)->keyHash >= 2) /* * A JSDHashTable is currently 8 words (without the JS_DHASHMETER overhead) * on most architectures, and may be allocated on the stack or within another * structure or class (see below for the Init and Finish functions to use). * * To decide whether to use double hashing vs. chaining, we need to develop a * trade-off relation, as follows: * * Let alpha be the load factor, esize the entry size in words, count the * entry count, and pow2 the power-of-two table size in entries. * * (JSDHashTable overhead) > (JSHashTable overhead) * (unused table entry space) > (malloc and .next overhead per entry) + * (buckets overhead) * (1 - alpha) * esize * pow2 > 2 * count + pow2 * * Notice that alpha is by definition (count / pow2): * * (1 - alpha) * esize * pow2 > 2 * alpha * pow2 + pow2 * (1 - alpha) * esize > 2 * alpha + 1 * * esize > (1 + 2 * alpha) / (1 - alpha) * * This assumes both tables must keep keyHash, key, and value for each entry, * where key and value point to separately allocated strings or structures. * If key and value can be combined into one pointer, then the trade-off is: * * esize > (1 + 3 * alpha) / (1 - alpha) * * If the entry value can be a subtype of JSDHashEntryHdr, rather than a type * that must be allocated separately and referenced by an entry.value pointer * member, and provided key's allocation can be fused with its entry's, then * k (the words wasted per entry with chaining) is 4. * * To see these curves, feed gnuplot input like so: * * gnuplot> f(x,k) = (1 + k * x) / (1 - x) * gnuplot> plot [0:.75] f(x,2), f(x,3), f(x,4) * * For k of 2 and a well-loaded table (alpha > .5), esize must be more than 4 * words for chaining to be more space-efficient than double hashing. * * Solving for alpha helps us decide when to shrink an underloaded table: * * esize > (1 + k * alpha) / (1 - alpha) * esize - alpha * esize > 1 + k * alpha * esize - 1 > (k + esize) * alpha * (esize - 1) / (k + esize) > alpha * * alpha < (esize - 1) / (esize + k) * * Therefore double hashing should keep alpha >= (esize - 1) / (esize + k), * assuming esize is not too large (in which case, chaining should probably be * used for any alpha). For esize=2 and k=3, we want alpha >= .2; for esize=3 * and k=2, we want alpha >= .4. For k=4, esize could be 6, and alpha >= .5 * would still obtain. See the JS_DHASH_MIN_ALPHA macro further below. * * The current implementation uses a configurable lower bound on alpha, which * defaults to .25, when deciding to shrink the table (while still respecting * JS_DHASH_MIN_SIZE). * * Note a qualitative difference between chaining and double hashing: under * chaining, entry addresses are stable across table shrinks and grows. With * double hashing, you can't safely hold an entry pointer and use it after an * ADD or REMOVE operation, unless you sample table->generation before adding * or removing, and compare the sample after, dereferencing the entry pointer * only if table->generation has not changed. * * The moral of this story: there is no one-size-fits-all hash table scheme, * but for small table entry size, and assuming entry address stability is not * required, double hashing wins. */ struct JSDHashTable { const JSDHashTableOps *ops; /* virtual operations, see below */ void *data; /* ops- and instance-specific data */ int16 hashShift; /* multiplicative hash shift */ uint8 maxAlphaFrac; /* 8-bit fixed point max alpha */ uint8 minAlphaFrac; /* 8-bit fixed point min alpha */ uint32 entrySize; /* number of bytes in an entry */ uint32 entryCount; /* number of entries in table */ uint32 removedCount; /* removed entry sentinels in table */ uint32 generation; /* entry storage generation number */ char *entryStore; /* entry storage */ #ifdef JS_DHASHMETER struct JSDHashStats { uint32 searches; /* total number of table searches */ uint32 steps; /* hash chain links traversed */ uint32 hits; /* searches that found key */ uint32 misses; /* searches that didn't find key */ uint32 lookups; /* number of JS_DHASH_LOOKUPs */ uint32 addMisses; /* adds that miss, and do work */ uint32 addOverRemoved; /* adds that recycled a removed entry */ uint32 addHits; /* adds that hit an existing entry */ uint32 addFailures; /* out-of-memory during add growth */ uint32 removeHits; /* removes that hit, and do work */ uint32 removeMisses; /* useless removes that miss */ uint32 removeFrees; /* removes that freed entry directly */ uint32 removeEnums; /* removes done by Enumerate */ uint32 grows; /* table expansions */ uint32 shrinks; /* table contractions */ uint32 compresses; /* table compressions */ uint32 enumShrinks; /* contractions after Enumerate */ } stats; #endif }; /* * Size in entries (gross, not net of free and removed sentinels) for table. * We store hashShift rather than sizeLog2 to optimize the collision-free case * in SearchTable. */ #define JS_DHASH_TABLE_SIZE(table) JS_BIT(JS_DHASH_BITS - (table)->hashShift) /* * Table space at entryStore is allocated and freed using these callbacks. * The allocator should return null on error only (not if called with nbytes * equal to 0; but note that jsdhash.c code will never call with 0 nbytes). */ typedef void * (* JS_DLL_CALLBACK JSDHashAllocTable)(JSDHashTable *table, uint32 nbytes); typedef void (* JS_DLL_CALLBACK JSDHashFreeTable) (JSDHashTable *table, void *ptr); /* * When a table grows or shrinks, each entry is queried for its key using this * callback. NB: in that event, entry is not in table any longer; it's in the * old entryStore vector, which is due to be freed once all entries have been * moved via moveEntry callbacks. */ typedef const void * (* JS_DLL_CALLBACK JSDHashGetKey) (JSDHashTable *table, JSDHashEntryHdr *entry); /* * Compute the hash code for a given key to be looked up, added, or removed * from table. A hash code may have any JSDHashNumber value. */ typedef JSDHashNumber (* JS_DLL_CALLBACK JSDHashHashKey) (JSDHashTable *table, const void *key); /* * Compare the key identifying entry in table with the provided key parameter. * Return JS_TRUE if keys match, JS_FALSE otherwise. */ typedef JSBool (* JS_DLL_CALLBACK JSDHashMatchEntry)(JSDHashTable *table, const JSDHashEntryHdr *entry, const void *key); /* * Copy the data starting at from to the new entry storage at to. Do not add * reference counts for any strong references in the entry, however, as this * is a "move" operation: the old entry storage at from will be freed without * any reference-decrementing callback shortly. */ typedef void (* JS_DLL_CALLBACK JSDHashMoveEntry)(JSDHashTable *table, const JSDHashEntryHdr *from, JSDHashEntryHdr *to); /* * Clear the entry and drop any strong references it holds. This callback is * invoked during a JS_DHASH_REMOVE operation (see below for operation codes), * but only if the given key is found in the table. */ typedef void (* JS_DLL_CALLBACK JSDHashClearEntry)(JSDHashTable *table, JSDHashEntryHdr *entry); /* * Called when a table (whether allocated dynamically by itself, or nested in * a larger structure, or allocated on the stack) is finished. This callback * allows table->ops-specific code to finalize table->data. */ typedef void (* JS_DLL_CALLBACK JSDHashFinalize) (JSDHashTable *table); /* * Initialize a new entry, apart from keyHash. This function is called when * JS_DHashTableOperate's JS_DHASH_ADD case finds no existing entry for the * given key, and must add a new one. At that point, entry->keyHash is not * set yet, to avoid claiming the last free entry in a severely overloaded * table. */ typedef JSBool (* JS_DLL_CALLBACK JSDHashInitEntry)(JSDHashTable *table, JSDHashEntryHdr *entry, const void *key); /* * Finally, the "vtable" structure for JSDHashTable. The first eight hooks * must be provided by implementations; they're called unconditionally by the * generic jsdhash.c code. Hooks after these may be null. * * Summary of allocation-related hook usage with C++ placement new emphasis: * allocTable Allocate raw bytes with malloc, no ctors run. * freeTable Free raw bytes with free, no dtors run. * initEntry Call placement new using default key-based ctor. * Return JS_TRUE on success, JS_FALSE on error. * moveEntry Call placement new using copy ctor, run dtor on old * entry storage. * clearEntry Run dtor on entry. * finalize Stub unless table->data was initialized and needs to * be finalized. * * Note the reason why initEntry is optional: the default hooks (stubs) clear * entry storage: On successful JS_DHashTableOperate(tbl, key, JS_DHASH_ADD), * the returned entry pointer addresses an entry struct whose keyHash member * has been set non-zero, but all other entry members are still clear (null). * JS_DHASH_ADD callers can test such members to see whether the entry was * newly created by the JS_DHASH_ADD call that just succeeded. If placement * new or similar initialization is required, define an initEntry hook. Of * course, the clearEntry hook must zero or null appropriately. * * XXX assumes 0 is null for pointer types. */ struct JSDHashTableOps { /* Mandatory hooks. All implementations must provide these. */ JSDHashAllocTable allocTable; JSDHashFreeTable freeTable; JSDHashGetKey getKey; JSDHashHashKey hashKey; JSDHashMatchEntry matchEntry; JSDHashMoveEntry moveEntry; JSDHashClearEntry clearEntry; JSDHashFinalize finalize; /* Optional hooks start here. If null, these are not called. */ JSDHashInitEntry initEntry; }; /* * Default implementations for the above ops. */ extern JS_PUBLIC_API(void *) JS_DHashAllocTable(JSDHashTable *table, uint32 nbytes); extern JS_PUBLIC_API(void) JS_DHashFreeTable(JSDHashTable *table, void *ptr); extern JS_PUBLIC_API(JSDHashNumber) JS_DHashStringKey(JSDHashTable *table, const void *key); /* A minimal entry contains a keyHash header and a void key pointer. */ struct JSDHashEntryStub { JSDHashEntryHdr hdr; const void *key; }; extern JS_PUBLIC_API(const void *) JS_DHashGetKeyStub(JSDHashTable *table, JSDHashEntryHdr *entry); extern JS_PUBLIC_API(JSDHashNumber) JS_DHashVoidPtrKeyStub(JSDHashTable *table, const void *key); extern JS_PUBLIC_API(JSBool) JS_DHashMatchEntryStub(JSDHashTable *table, const JSDHashEntryHdr *entry, const void *key); extern JS_PUBLIC_API(JSBool) JS_DHashMatchStringKey(JSDHashTable *table, const JSDHashEntryHdr *entry, const void *key); extern JS_PUBLIC_API(void) JS_DHashMoveEntryStub(JSDHashTable *table, const JSDHashEntryHdr *from, JSDHashEntryHdr *to); extern JS_PUBLIC_API(void) JS_DHashClearEntryStub(JSDHashTable *table, JSDHashEntryHdr *entry); extern JS_PUBLIC_API(void) JS_DHashFreeStringKey(JSDHashTable *table, JSDHashEntryHdr *entry); extern JS_PUBLIC_API(void) JS_DHashFinalizeStub(JSDHashTable *table); /* * If you use JSDHashEntryStub or a subclass of it as your entry struct, and * if your entries move via memcpy and clear via memset(0), you can use these * stub operations. */ extern JS_PUBLIC_API(const JSDHashTableOps *) JS_DHashGetStubOps(void); /* * Dynamically allocate a new JSDHashTable using malloc, initialize it using * JS_DHashTableInit, and return its address. Return null on malloc failure. * Note that the entry storage at table->entryStore will be allocated using * the ops->allocTable callback. */ extern JS_PUBLIC_API(JSDHashTable *) JS_NewDHashTable(const JSDHashTableOps *ops, void *data, uint32 entrySize, uint32 capacity); /* * Finalize table's data, free its entry storage (via table->ops->freeTable), * and return the memory starting at table to the malloc heap. */ extern JS_PUBLIC_API(void) JS_DHashTableDestroy(JSDHashTable *table); /* * Initialize table with ops, data, entrySize, and capacity. Capacity is a * guess for the smallest table size at which the table will usually be less * than 75% loaded (the table will grow or shrink as needed; capacity serves * only to avoid inevitable early growth from JS_DHASH_MIN_SIZE). */ extern JS_PUBLIC_API(JSBool) JS_DHashTableInit(JSDHashTable *table, const JSDHashTableOps *ops, void *data, uint32 entrySize, uint32 capacity); /* * Set maximum and minimum alpha for table. The defaults are 0.75 and .25. * maxAlpha must be in [0.5, 0.9375] for the default JS_DHASH_MIN_SIZE; or if * MinSize=JS_DHASH_MIN_SIZE <= 256, in [0.5, (float)(MinSize-1)/MinSize]; or * else in [0.5, 255.0/256]. minAlpha must be in [0, maxAlpha / 2), so that * we don't shrink on the very next remove after growing a table upon adding * an entry that brings entryCount past maxAlpha * tableSize. */ extern JS_PUBLIC_API(void) JS_DHashTableSetAlphaBounds(JSDHashTable *table, float maxAlpha, float minAlpha); /* * Call this macro with k, the number of pointer-sized words wasted per entry * under chaining, to compute the minimum alpha at which double hashing still * beats chaining. */ #define JS_DHASH_MIN_ALPHA(table, k) \ ((float)((table)->entrySize / sizeof(void *) - 1) \ / ((table)->entrySize / sizeof(void *) + (k))) /* * Finalize table's data, free its entry storage using table->ops->freeTable, * and leave its members unchanged from their last live values (which leaves * pointers dangling). If you want to burn cycles clearing table, it's up to * your code to call memset. */ extern JS_PUBLIC_API(void) JS_DHashTableFinish(JSDHashTable *table); /* * To consolidate keyHash computation and table grow/shrink code, we use a * single entry point for lookup, add, and remove operations. The operation * codes are declared here, along with codes returned by JSDHashEnumerator * functions, which control JS_DHashTableEnumerate's behavior. */ typedef enum JSDHashOperator { JS_DHASH_LOOKUP = 0, /* lookup entry */ JS_DHASH_ADD = 1, /* add entry */ JS_DHASH_REMOVE = 2, /* remove entry, or enumerator says remove */ JS_DHASH_NEXT = 0, /* enumerator says continue */ JS_DHASH_STOP = 1 /* enumerator says stop */ } JSDHashOperator; /* * To lookup a key in table, call: * * entry = JS_DHashTableOperate(table, key, JS_DHASH_LOOKUP); * * If JS_DHASH_ENTRY_IS_BUSY(entry) is true, key was found and it identifies * entry. If JS_DHASH_ENTRY_IS_FREE(entry) is true, key was not found. * * To add an entry identified by key to table, call: * * entry = JS_DHashTableOperate(table, key, JS_DHASH_ADD); * * If entry is null upon return, then either the table is severely overloaded, * and memory can't be allocated for entry storage via table->ops->allocTable; * Or if table->ops->initEntry is non-null, the table->ops->initEntry op may * have returned false. * * Otherwise, entry->keyHash has been set so that JS_DHASH_ENTRY_IS_BUSY(entry) * is true, and it is up to the caller to initialize the key and value parts * of the entry sub-type, if they have not been set already (i.e. if entry was * not already in the table, and if the optional initEntry hook was not used). * * To remove an entry identified by key from table, call: * * (void) JS_DHashTableOperate(table, key, JS_DHASH_REMOVE); * * If key's entry is found, it is cleared (via table->ops->clearEntry) and * the entry is marked so that JS_DHASH_ENTRY_IS_FREE(entry). This operation * returns null unconditionally; you should ignore its return value. */ extern JS_PUBLIC_API(JSDHashEntryHdr *) JS_DHASH_FASTCALL JS_DHashTableOperate(JSDHashTable *table, const void *key, JSDHashOperator op); /* * Remove an entry already accessed via LOOKUP or ADD. * * NB: this is a "raw" or low-level routine, intended to be used only where * the inefficiency of a full JS_DHashTableOperate (which rehashes in order * to find the entry given its key) is not tolerable. This function does not * shrink the table if it is underloaded. It does not update stats #ifdef * JS_DHASHMETER, either. */ extern JS_PUBLIC_API(void) JS_DHashTableRawRemove(JSDHashTable *table, JSDHashEntryHdr *entry); /* * Enumerate entries in table using etor: * * count = JS_DHashTableEnumerate(table, etor, arg); * * JS_DHashTableEnumerate calls etor like so: * * op = etor(table, entry, number, arg); * * where number is a zero-based ordinal assigned to live entries according to * their order in table->entryStore. * * The return value, op, is treated as a set of flags. If op is JS_DHASH_NEXT, * then continue enumerating. If op contains JS_DHASH_REMOVE, then clear (via * table->ops->clearEntry) and free entry. Then we check whether op contains * JS_DHASH_STOP; if so, stop enumerating and return the number of live entries * that were enumerated so far. Return the total number of live entries when * enumeration completes normally. * * If etor calls JS_DHashTableOperate on table with op != JS_DHASH_LOOKUP, it * must return JS_DHASH_STOP; otherwise undefined behavior results. * * If any enumerator returns JS_DHASH_REMOVE, table->entryStore may be shrunk * or compressed after enumeration, but before JS_DHashTableEnumerate returns. * Such an enumerator therefore can't safely set aside entry pointers, but an * enumerator that never returns JS_DHASH_REMOVE can set pointers to entries * aside, e.g., to avoid copying live entries into an array of the entry type. * Copying entry pointers is cheaper, and safe so long as the caller of such a * "stable" Enumerate doesn't use the set-aside pointers after any call either * to PL_DHashTableOperate, or to an "unstable" form of Enumerate, which might * grow or shrink entryStore. * * If your enumerator wants to remove certain entries, but set aside pointers * to other entries that it retains, it can use JS_DHashTableRawRemove on the * entries to be removed, returning JS_DHASH_NEXT to skip them. Likewise, if * you want to remove entries, but for some reason you do not want entryStore * to be shrunk or compressed, you can call JS_DHashTableRawRemove safely on * the entry being enumerated, rather than returning JS_DHASH_REMOVE. */ typedef JSDHashOperator (* JS_DLL_CALLBACK JSDHashEnumerator)(JSDHashTable *table, JSDHashEntryHdr *hdr, uint32 number, void *arg); extern JS_PUBLIC_API(uint32) JS_DHashTableEnumerate(JSDHashTable *table, JSDHashEnumerator etor, void *arg); #ifdef JS_DHASHMETER #include extern JS_PUBLIC_API(void) JS_DHashTableDumpMeter(JSDHashTable *table, JSDHashEnumerator dump, FILE *fp); #endif JS_END_EXTERN_C #endif /* jsdhash_h___ */ pacparser-1.4.5/src/spidermonkey/js/src/jsdtoa.c000066400000000000000000002551621464010763600216560ustar00rootroot00000000000000/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- * * ***** BEGIN LICENSE BLOCK ***** * Version: MPL 1.1/GPL 2.0/LGPL 2.1 * * The contents of this file are subject to the Mozilla Public License Version * 1.1 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * http://www.mozilla.org/MPL/ * * Software distributed under the License is distributed on an "AS IS" basis, * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License * for the specific language governing rights and limitations under the * License. * * The Original Code is Mozilla Communicator client code, released * March 31, 1998. * * The Initial Developer of the Original Code is * Netscape Communications Corporation. * Portions created by the Initial Developer are Copyright (C) 1998 * the Initial Developer. All Rights Reserved. * * Contributor(s): * * Alternatively, the contents of this file may be used under the terms of * either of the GNU General Public License Version 2 or later (the "GPL"), * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), * in which case the provisions of the GPL or the LGPL are applicable instead * of those above. If you wish to allow use of your version of this file only * under the terms of either the GPL or the LGPL, and not to allow others to * use your version of this file under the terms of the MPL, indicate your * decision by deleting the provisions above and replace them with the notice * and other provisions required by the GPL or the LGPL. If you do not delete * the provisions above, a recipient may use your version of this file under * the terms of any one of the MPL, the GPL or the LGPL. * * ***** END LICENSE BLOCK ***** */ /* * Portable double to alphanumeric string and back converters. */ #include "jsstddef.h" #include "jslibmath.h" #include "jstypes.h" #include "jsdtoa.h" #include "jsprf.h" #include "jsutil.h" /* Added by JSIFY */ #include "jspubtd.h" #include "jsnum.h" #ifdef JS_THREADSAFE #include "prlock.h" #endif /**************************************************************** * * The author of this software is David M. Gay. * * Copyright (c) 1991 by Lucent Technologies. * * Permission to use, copy, modify, and distribute this software for any * purpose without fee is hereby granted, provided that this entire notice * is included in all copies of any software which is or includes a copy * or modification of this software and in all copies of the supporting * documentation for such software. * * THIS SOFTWARE IS BEING PROVIDED "AS IS", WITHOUT ANY EXPRESS OR IMPLIED * WARRANTY. IN PARTICULAR, NEITHER THE AUTHOR NOR LUCENT MAKES ANY * REPRESENTATION OR WARRANTY OF ANY KIND CONCERNING THE MERCHANTABILITY * OF THIS SOFTWARE OR ITS FITNESS FOR ANY PARTICULAR PURPOSE. * ***************************************************************/ /* Please send bug reports to David M. Gay Bell Laboratories, Room 2C-463 600 Mountain Avenue Murray Hill, NJ 07974-0636 U.S.A. dmg@bell-labs.com */ /* On a machine with IEEE extended-precision registers, it is * necessary to specify double-precision (53-bit) rounding precision * before invoking strtod or dtoa. If the machine uses (the equivalent * of) Intel 80x87 arithmetic, the call * _control87(PC_53, MCW_PC); * does this with many compilers. Whether this or another call is * appropriate depends on the compiler; for this to work, it may be * necessary to #include "float.h" or another system-dependent header * file. */ /* strtod for IEEE-arithmetic machines. * * This strtod returns a nearest machine number to the input decimal * string (or sets err to JS_DTOA_ERANGE or JS_DTOA_ENOMEM). With IEEE * arithmetic, ties are broken by the IEEE round-even rule. Otherwise * ties are broken by biased rounding (add half and chop). * * Inspired loosely by William D. Clinger's paper "How to Read Floating * Point Numbers Accurately" [Proc. ACM SIGPLAN '90, pp. 92-101]. * * Modifications: * * 1. We only require IEEE double-precision * arithmetic (not IEEE double-extended). * 2. We get by with floating-point arithmetic in a case that * Clinger missed -- when we're computing d * 10^n * for a small integer d and the integer n is not too * much larger than 22 (the maximum integer k for which * we can represent 10^k exactly), we may be able to * compute (d*10^k) * 10^(e-k) with just one roundoff. * 3. Rather than a bit-at-a-time adjustment of the binary * result in the hard case, we use floating-point * arithmetic to determine the adjustment to within * one bit; only in really hard cases do we need to * compute a second residual. * 4. Because of 3., we don't need a large table of powers of 10 * for ten-to-e (just some small tables, e.g. of 10^k * for 0 <= k <= 22). */ /* * #define IEEE_8087 for IEEE-arithmetic machines where the least * significant byte has the lowest address. * #define IEEE_MC68k for IEEE-arithmetic machines where the most * significant byte has the lowest address. * #define Long int on machines with 32-bit ints and 64-bit longs. * #define Sudden_Underflow for IEEE-format machines without gradual * underflow (i.e., that flush to zero on underflow). * #define No_leftright to omit left-right logic in fast floating-point * computation of js_dtoa. * #define Check_FLT_ROUNDS if FLT_ROUNDS can assume the values 2 or 3. * #define RND_PRODQUOT to use rnd_prod and rnd_quot (assembly routines * that use extended-precision instructions to compute rounded * products and quotients) with IBM. * #define ROUND_BIASED for IEEE-format with biased rounding. * #define Inaccurate_Divide for IEEE-format with correctly rounded * products but inaccurate quotients, e.g., for Intel i860. * #define JS_HAVE_LONG_LONG on machines that have a "long long" * integer type (of >= 64 bits). If long long is available and the name is * something other than "long long", #define Llong to be the name, * and if "unsigned Llong" does not work as an unsigned version of * Llong, #define #ULLong to be the corresponding unsigned type. * #define Bad_float_h if your system lacks a float.h or if it does not * define some or all of DBL_DIG, DBL_MAX_10_EXP, DBL_MAX_EXP, * FLT_RADIX, FLT_ROUNDS, and DBL_MAX. * #define MALLOC your_malloc, where your_malloc(n) acts like malloc(n) * if memory is available and otherwise does something you deem * appropriate. If MALLOC is undefined, malloc will be invoked * directly -- and assumed always to succeed. * #define Omit_Private_Memory to omit logic (added Jan. 1998) for making * memory allocations from a private pool of memory when possible. * When used, the private pool is PRIVATE_MEM bytes long: 2000 bytes, * unless #defined to be a different length. This default length * suffices to get rid of MALLOC calls except for unusual cases, * such as decimal-to-binary conversion of a very long string of * digits. * #define INFNAN_CHECK on IEEE systems to cause strtod to check for * Infinity and NaN (case insensitively). On some systems (e.g., * some HP systems), it may be necessary to #define NAN_WORD0 * appropriately -- to the most significant word of a quiet NaN. * (On HP Series 700/800 machines, -DNAN_WORD0=0x7ff40000 works.) * #define MULTIPLE_THREADS if the system offers preemptively scheduled * multiple threads. In this case, you must provide (or suitably * #define) two locks, acquired by ACQUIRE_DTOA_LOCK() and released * by RELEASE_DTOA_LOCK(). (The second lock, accessed * in pow5mult, ensures lazy evaluation of only one copy of high * powers of 5; omitting this lock would introduce a small * probability of wasting memory, but would otherwise be harmless.) * You must also invoke freedtoa(s) to free the value s returned by * dtoa. You may do so whether or not MULTIPLE_THREADS is #defined. * #define NO_IEEE_Scale to disable new (Feb. 1997) logic in strtod that * avoids underflows on inputs whose result does not underflow. */ #ifdef IS_LITTLE_ENDIAN #define IEEE_8087 #else #define IEEE_MC68k #endif #ifndef Long #define Long int32 #endif #ifndef ULong #define ULong uint32 #endif #define Bug(errorMessageString) JS_ASSERT(!errorMessageString) #include "stdlib.h" #include "string.h" #ifdef MALLOC extern void *MALLOC(size_t); #else #define MALLOC malloc #endif #define Omit_Private_Memory /* Private memory currently doesn't work with JS_THREADSAFE */ #ifndef Omit_Private_Memory #ifndef PRIVATE_MEM #define PRIVATE_MEM 2000 #endif #define PRIVATE_mem ((PRIVATE_MEM+sizeof(double)-1)/sizeof(double)) static double private_mem[PRIVATE_mem], *pmem_next = private_mem; #endif #ifdef Bad_float_h #undef __STDC__ #define DBL_DIG 15 #define DBL_MAX_10_EXP 308 #define DBL_MAX_EXP 1024 #define FLT_RADIX 2 #define FLT_ROUNDS 1 #define DBL_MAX 1.7976931348623157e+308 #ifndef LONG_MAX #define LONG_MAX 2147483647 #endif #else /* ifndef Bad_float_h */ #include "float.h" #endif /* Bad_float_h */ #ifndef __MATH_H__ #include "math.h" #endif #ifndef CONST #define CONST const #endif #if defined(IEEE_8087) + defined(IEEE_MC68k) != 1 Exactly one of IEEE_8087 or IEEE_MC68k should be defined. #endif #define word0(x) JSDOUBLE_HI32(x) #define set_word0(x, y) JSDOUBLE_SET_HI32(x, y) #define word1(x) JSDOUBLE_LO32(x) #define set_word1(x, y) JSDOUBLE_SET_LO32(x, y) #define Storeinc(a,b,c) (*(a)++ = (b) << 16 | (c) & 0xffff) /* #define P DBL_MANT_DIG */ /* Ten_pmax = floor(P*log(2)/log(5)) */ /* Bletch = (highest power of 2 < DBL_MAX_10_EXP) / 16 */ /* Quick_max = floor((P-1)*log(FLT_RADIX)/log(10) - 1) */ /* Int_max = floor(P*log(FLT_RADIX)/log(10) - 1) */ #define Exp_shift 20 #define Exp_shift1 20 #define Exp_msk1 0x100000 #define Exp_msk11 0x100000 #define Exp_mask 0x7ff00000 #define P 53 #define Bias 1023 #define Emin (-1022) #define Exp_1 0x3ff00000 #define Exp_11 0x3ff00000 #define Ebits 11 #define Frac_mask 0xfffff #define Frac_mask1 0xfffff #define Ten_pmax 22 #define Bletch 0x10 #define Bndry_mask 0xfffff #define Bndry_mask1 0xfffff #define LSB 1 #define Sign_bit 0x80000000 #define Log2P 1 #define Tiny0 0 #define Tiny1 1 #define Quick_max 14 #define Int_max 14 #define Infinite(x) (word0(x) == 0x7ff00000) /* sufficient test for here */ #ifndef NO_IEEE_Scale #define Avoid_Underflow #endif #ifdef RND_PRODQUOT #define rounded_product(a,b) a = rnd_prod(a, b) #define rounded_quotient(a,b) a = rnd_quot(a, b) extern double rnd_prod(double, double), rnd_quot(double, double); #else #define rounded_product(a,b) a *= b #define rounded_quotient(a,b) a /= b #endif #define Big0 (Frac_mask1 | Exp_msk1*(DBL_MAX_EXP+Bias-1)) #define Big1 0xffffffff #ifndef JS_HAVE_LONG_LONG #undef ULLong #else /* long long available */ #ifndef Llong #define Llong JSInt64 #endif #ifndef ULLong #define ULLong JSUint64 #endif #endif /* JS_HAVE_LONG_LONG */ #ifdef JS_THREADSAFE #define MULTIPLE_THREADS static PRLock *freelist_lock; #define ACQUIRE_DTOA_LOCK() \ JS_BEGIN_MACRO \ if (!initialized) \ InitDtoa(); \ PR_Lock(freelist_lock); \ JS_END_MACRO #define RELEASE_DTOA_LOCK() PR_Unlock(freelist_lock) #else #undef MULTIPLE_THREADS #define ACQUIRE_DTOA_LOCK() /*nothing*/ #define RELEASE_DTOA_LOCK() /*nothing*/ #endif #define Kmax 15 struct Bigint { struct Bigint *next; /* Free list link */ int32 k; /* lg2(maxwds) */ int32 maxwds; /* Number of words allocated for x */ int32 sign; /* Zero if positive, 1 if negative. Ignored by most Bigint routines! */ int32 wds; /* Actual number of words. If value is nonzero, the most significant word must be nonzero. */ ULong x[1]; /* wds words of number in little endian order */ }; #ifdef ENABLE_OOM_TESTING /* Out-of-memory testing. Use a good testcase (over and over) and then use * these routines to cause a memory failure on every possible Balloc allocation, * to make sure that all out-of-memory paths can be followed. See bug 14044. */ static int allocationNum; /* which allocation is next? */ static int desiredFailure; /* which allocation should fail? */ /** * js_BigintTestingReset * * Call at the beginning of a test run to set the allocation failure position. * (Set to 0 to just have the engine count allocations without failing.) */ JS_PUBLIC_API(void) js_BigintTestingReset(int newFailure) { allocationNum = 0; desiredFailure = newFailure; } /** * js_BigintTestingWhere * * Report the current allocation position. This is really only useful when you * want to learn how many allocations a test run has. */ JS_PUBLIC_API(int) js_BigintTestingWhere() { return allocationNum; } /* * So here's what you do: Set up a fantastic test case that exercises the * elements of the code you wish. Set the failure point at 0 and run the test, * then get the allocation position. This number is the number of allocations * your test makes. Now loop from 1 to that number, setting the failure point * at each loop count, and run the test over and over, causing failures at each * step. Any memory failure *should* cause a Out-Of-Memory exception; if it * doesn't, then there's still an error here. */ #endif typedef struct Bigint Bigint; static Bigint *freelist[Kmax+1]; /* * Allocate a Bigint with 2^k words. * This is not threadsafe. The caller must use thread locks */ static Bigint *Balloc(int32 k) { int32 x; Bigint *rv; #ifndef Omit_Private_Memory uint32 len; #endif #ifdef ENABLE_OOM_TESTING if (++allocationNum == desiredFailure) { printf("Forced Failing Allocation number %d\n", allocationNum); return NULL; } #endif if ((rv = freelist[k]) != NULL) freelist[k] = rv->next; if (rv == NULL) { x = 1 << k; #ifdef Omit_Private_Memory rv = (Bigint *)MALLOC(sizeof(Bigint) + (x-1)*sizeof(ULong)); #else len = (sizeof(Bigint) + (x-1)*sizeof(ULong) + sizeof(double) - 1) /sizeof(double); if (pmem_next - private_mem + len <= PRIVATE_mem) { rv = (Bigint*)pmem_next; pmem_next += len; } else rv = (Bigint*)MALLOC(len*sizeof(double)); #endif if (!rv) return NULL; rv->k = k; rv->maxwds = x; } rv->sign = rv->wds = 0; return rv; } static void Bfree(Bigint *v) { if (v) { v->next = freelist[v->k]; freelist[v->k] = v; } } #define Bcopy(x,y) memcpy((char *)&x->sign, (char *)&y->sign, \ y->wds*sizeof(Long) + 2*sizeof(int32)) /* Return b*m + a. Deallocate the old b. Both a and m must be between 0 and * 65535 inclusive. NOTE: old b is deallocated on memory failure. */ static Bigint *multadd(Bigint *b, int32 m, int32 a) { int32 i, wds; #ifdef ULLong ULong *x; ULLong carry, y; #else ULong carry, *x, y; ULong xi, z; #endif Bigint *b1; #ifdef ENABLE_OOM_TESTING if (++allocationNum == desiredFailure) { /* Faux allocation, because I'm not getting all of the failure paths * without it. */ printf("Forced Failing Allocation number %d\n", allocationNum); Bfree(b); return NULL; } #endif wds = b->wds; x = b->x; i = 0; carry = a; do { #ifdef ULLong y = *x * (ULLong)m + carry; carry = y >> 32; *x++ = (ULong)(y & 0xffffffffUL); #else xi = *x; y = (xi & 0xffff) * m + carry; z = (xi >> 16) * m + (y >> 16); carry = z >> 16; *x++ = (z << 16) + (y & 0xffff); #endif } while(++i < wds); if (carry) { if (wds >= b->maxwds) { b1 = Balloc(b->k+1); if (!b1) { Bfree(b); return NULL; } Bcopy(b1, b); Bfree(b); b = b1; } b->x[wds++] = (ULong)carry; b->wds = wds; } return b; } static Bigint *s2b(CONST char *s, int32 nd0, int32 nd, ULong y9) { Bigint *b; int32 i, k; Long x, y; x = (nd + 8) / 9; for(k = 0, y = 1; x > y; y <<= 1, k++) ; b = Balloc(k); if (!b) return NULL; b->x[0] = y9; b->wds = 1; i = 9; if (9 < nd0) { s += 9; do { b = multadd(b, 10, *s++ - '0'); if (!b) return NULL; } while(++i < nd0); s++; } else s += 10; for(; i < nd; i++) { b = multadd(b, 10, *s++ - '0'); if (!b) return NULL; } return b; } /* Return the number (0 through 32) of most significant zero bits in x. */ static int32 hi0bits(register ULong x) { register int32 k = 0; if (!(x & 0xffff0000)) { k = 16; x <<= 16; } if (!(x & 0xff000000)) { k += 8; x <<= 8; } if (!(x & 0xf0000000)) { k += 4; x <<= 4; } if (!(x & 0xc0000000)) { k += 2; x <<= 2; } if (!(x & 0x80000000)) { k++; if (!(x & 0x40000000)) return 32; } return k; } /* Return the number (0 through 32) of least significant zero bits in y. * Also shift y to the right past these 0 through 32 zeros so that y's * least significant bit will be set unless y was originally zero. */ static int32 lo0bits(ULong *y) { register int32 k; register ULong x = *y; if (x & 7) { if (x & 1) return 0; if (x & 2) { *y = x >> 1; return 1; } *y = x >> 2; return 2; } k = 0; if (!(x & 0xffff)) { k = 16; x >>= 16; } if (!(x & 0xff)) { k += 8; x >>= 8; } if (!(x & 0xf)) { k += 4; x >>= 4; } if (!(x & 0x3)) { k += 2; x >>= 2; } if (!(x & 1)) { k++; x >>= 1; if (!x & 1) return 32; } *y = x; return k; } /* Return a new Bigint with the given integer value, which must be nonnegative. */ static Bigint *i2b(int32 i) { Bigint *b; b = Balloc(1); if (!b) return NULL; b->x[0] = i; b->wds = 1; return b; } /* Return a newly allocated product of a and b. */ static Bigint *mult(CONST Bigint *a, CONST Bigint *b) { CONST Bigint *t; Bigint *c; int32 k, wa, wb, wc; ULong y; ULong *xc, *xc0, *xce; CONST ULong *x, *xa, *xae, *xb, *xbe; #ifdef ULLong ULLong carry, z; #else ULong carry, z; ULong z2; #endif if (a->wds < b->wds) { t = a; a = b; b = t; } k = a->k; wa = a->wds; wb = b->wds; wc = wa + wb; if (wc > a->maxwds) k++; c = Balloc(k); if (!c) return NULL; for(xc = c->x, xce = xc + wc; xc < xce; xc++) *xc = 0; xa = a->x; xae = xa + wa; xb = b->x; xbe = xb + wb; xc0 = c->x; #ifdef ULLong for(; xb < xbe; xc0++) { if ((y = *xb++) != 0) { x = xa; xc = xc0; carry = 0; do { z = *x++ * (ULLong)y + *xc + carry; carry = z >> 32; *xc++ = (ULong)(z & 0xffffffffUL); } while(x < xae); *xc = (ULong)carry; } } #else for(; xb < xbe; xb++, xc0++) { if ((y = *xb & 0xffff) != 0) { x = xa; xc = xc0; carry = 0; do { z = (*x & 0xffff) * y + (*xc & 0xffff) + carry; carry = z >> 16; z2 = (*x++ >> 16) * y + (*xc >> 16) + carry; carry = z2 >> 16; Storeinc(xc, z2, z); } while(x < xae); *xc = carry; } if ((y = *xb >> 16) != 0) { x = xa; xc = xc0; carry = 0; z2 = *xc; do { z = (*x & 0xffff) * y + (*xc >> 16) + carry; carry = z >> 16; Storeinc(xc, z, z2); z2 = (*x++ >> 16) * y + (*xc & 0xffff) + carry; carry = z2 >> 16; } while(x < xae); *xc = z2; } } #endif for(xc0 = c->x, xc = xc0 + wc; wc > 0 && !*--xc; --wc) ; c->wds = wc; return c; } /* * 'p5s' points to a linked list of Bigints that are powers of 5. * This list grows on demand, and it can only grow: it won't change * in any other way. So if we read 'p5s' or the 'next' field of * some Bigint on the list, and it is not NULL, we know it won't * change to NULL or some other value. Only when the value of * 'p5s' or 'next' is NULL do we need to acquire the lock and add * a new Bigint to the list. */ static Bigint *p5s; #ifdef JS_THREADSAFE static PRLock *p5s_lock; #endif /* Return b * 5^k. Deallocate the old b. k must be nonnegative. */ /* NOTE: old b is deallocated on memory failure. */ static Bigint *pow5mult(Bigint *b, int32 k) { Bigint *b1, *p5, *p51; int32 i; static CONST int32 p05[3] = { 5, 25, 125 }; if ((i = k & 3) != 0) { b = multadd(b, p05[i-1], 0); if (!b) return NULL; } if (!(k >>= 2)) return b; if (!(p5 = p5s)) { #ifdef JS_THREADSAFE /* * We take great care to not call i2b() and Bfree() * while holding the lock. */ Bigint *wasted_effort = NULL; p5 = i2b(625); if (!p5) { Bfree(b); return NULL; } /* lock and check again */ PR_Lock(p5s_lock); if (!p5s) { /* first time */ p5s = p5; p5->next = 0; } else { /* some other thread just beat us */ wasted_effort = p5; p5 = p5s; } PR_Unlock(p5s_lock); if (wasted_effort) { Bfree(wasted_effort); } #else /* first time */ p5 = p5s = i2b(625); if (!p5) { Bfree(b); return NULL; } p5->next = 0; #endif } for(;;) { if (k & 1) { b1 = mult(b, p5); Bfree(b); if (!b1) return NULL; b = b1; } if (!(k >>= 1)) break; if (!(p51 = p5->next)) { #ifdef JS_THREADSAFE Bigint *wasted_effort = NULL; p51 = mult(p5, p5); if (!p51) { Bfree(b); return NULL; } PR_Lock(p5s_lock); if (!p5->next) { p5->next = p51; p51->next = 0; } else { wasted_effort = p51; p51 = p5->next; } PR_Unlock(p5s_lock); if (wasted_effort) { Bfree(wasted_effort); } #else p51 = mult(p5,p5); if (!p51) { Bfree(b); return NULL; } p51->next = 0; p5->next = p51; #endif } p5 = p51; } return b; } /* Return b * 2^k. Deallocate the old b. k must be nonnegative. * NOTE: on memory failure, old b is deallocated. */ static Bigint *lshift(Bigint *b, int32 k) { int32 i, k1, n, n1; Bigint *b1; ULong *x, *x1, *xe, z; n = k >> 5; k1 = b->k; n1 = n + b->wds + 1; for(i = b->maxwds; n1 > i; i <<= 1) k1++; b1 = Balloc(k1); if (!b1) goto done; x1 = b1->x; for(i = 0; i < n; i++) *x1++ = 0; x = b->x; xe = x + b->wds; if (k &= 0x1f) { k1 = 32 - k; z = 0; do { *x1++ = *x << k | z; z = *x++ >> k1; } while(x < xe); if ((*x1 = z) != 0) ++n1; } else do *x1++ = *x++; while(x < xe); b1->wds = n1 - 1; done: Bfree(b); return b1; } /* Return -1, 0, or 1 depending on whether ab, respectively. */ static int32 cmp(Bigint *a, Bigint *b) { ULong *xa, *xa0, *xb, *xb0; int32 i, j; i = a->wds; j = b->wds; #ifdef DEBUG if (i > 1 && !a->x[i-1]) Bug("cmp called with a->x[a->wds-1] == 0"); if (j > 1 && !b->x[j-1]) Bug("cmp called with b->x[b->wds-1] == 0"); #endif if (i -= j) return i; xa0 = a->x; xa = xa0 + j; xb0 = b->x; xb = xb0 + j; for(;;) { if (*--xa != *--xb) return *xa < *xb ? -1 : 1; if (xa <= xa0) break; } return 0; } static Bigint *diff(Bigint *a, Bigint *b) { Bigint *c; int32 i, wa, wb; ULong *xa, *xae, *xb, *xbe, *xc; #ifdef ULLong ULLong borrow, y; #else ULong borrow, y; ULong z; #endif i = cmp(a,b); if (!i) { c = Balloc(0); if (!c) return NULL; c->wds = 1; c->x[0] = 0; return c; } if (i < 0) { c = a; a = b; b = c; i = 1; } else i = 0; c = Balloc(a->k); if (!c) return NULL; c->sign = i; wa = a->wds; xa = a->x; xae = xa + wa; wb = b->wds; xb = b->x; xbe = xb + wb; xc = c->x; borrow = 0; #ifdef ULLong do { y = (ULLong)*xa++ - *xb++ - borrow; borrow = y >> 32 & 1UL; *xc++ = (ULong)(y & 0xffffffffUL); } while(xb < xbe); while(xa < xae) { y = *xa++ - borrow; borrow = y >> 32 & 1UL; *xc++ = (ULong)(y & 0xffffffffUL); } #else do { y = (*xa & 0xffff) - (*xb & 0xffff) - borrow; borrow = (y & 0x10000) >> 16; z = (*xa++ >> 16) - (*xb++ >> 16) - borrow; borrow = (z & 0x10000) >> 16; Storeinc(xc, z, y); } while(xb < xbe); while(xa < xae) { y = (*xa & 0xffff) - borrow; borrow = (y & 0x10000) >> 16; z = (*xa++ >> 16) - borrow; borrow = (z & 0x10000) >> 16; Storeinc(xc, z, y); } #endif while(!*--xc) wa--; c->wds = wa; return c; } /* Return the absolute difference between x and the adjacent greater-magnitude double number (ignoring exponent overflows). */ static double ulp(double x) { register Long L; double a = 0; L = (word0(x) & Exp_mask) - (P-1)*Exp_msk1; #ifndef Sudden_Underflow if (L > 0) { #endif set_word0(a, L); set_word1(a, 0); #ifndef Sudden_Underflow } else { L = -L >> Exp_shift; if (L < Exp_shift) { set_word0(a, 0x80000 >> L); set_word1(a, 0); } else { set_word0(a, 0); L -= Exp_shift; set_word1(a, L >= 31 ? 1 : 1 << (31 - L)); } } #endif return a; } static double b2d(Bigint *a, int32 *e) { ULong *xa, *xa0, w, y, z; int32 k; double d = 0; #define d0 word0(d) #define d1 word1(d) #define set_d0(x) set_word0(d, x) #define set_d1(x) set_word1(d, x) xa0 = a->x; xa = xa0 + a->wds; y = *--xa; #ifdef DEBUG if (!y) Bug("zero y in b2d"); #endif k = hi0bits(y); *e = 32 - k; if (k < Ebits) { set_d0(Exp_1 | y >> (Ebits - k)); w = xa > xa0 ? *--xa : 0; set_d1(y << (32-Ebits + k) | w >> (Ebits - k)); goto ret_d; } z = xa > xa0 ? *--xa : 0; if (k -= Ebits) { set_d0(Exp_1 | y << k | z >> (32 - k)); y = xa > xa0 ? *--xa : 0; set_d1(z << k | y >> (32 - k)); } else { set_d0(Exp_1 | y); set_d1(z); } ret_d: #undef d0 #undef d1 #undef set_d0 #undef set_d1 return d; } /* Convert d into the form b*2^e, where b is an odd integer. b is the returned * Bigint and e is the returned binary exponent. Return the number of significant * bits in b in bits. d must be finite and nonzero. */ static Bigint *d2b(double d, int32 *e, int32 *bits) { Bigint *b; int32 de, i, k; ULong *x, y, z; #define d0 word0(d) #define d1 word1(d) #define set_d0(x) set_word0(d, x) #define set_d1(x) set_word1(d, x) b = Balloc(1); if (!b) return NULL; x = b->x; z = d0 & Frac_mask; set_d0(d0 & 0x7fffffff); /* clear sign bit, which we ignore */ #ifdef Sudden_Underflow de = (int32)(d0 >> Exp_shift); z |= Exp_msk11; #else if ((de = (int32)(d0 >> Exp_shift)) != 0) z |= Exp_msk1; #endif if ((y = d1) != 0) { if ((k = lo0bits(&y)) != 0) { x[0] = y | z << (32 - k); z >>= k; } else x[0] = y; i = b->wds = (x[1] = z) ? 2 : 1; } else { JS_ASSERT(z); k = lo0bits(&z); x[0] = z; i = b->wds = 1; k += 32; } #ifndef Sudden_Underflow if (de) { #endif *e = de - Bias - (P-1) + k; *bits = P - k; #ifndef Sudden_Underflow } else { *e = de - Bias - (P-1) + 1 + k; *bits = 32*i - hi0bits(x[i-1]); } #endif return b; } #undef d0 #undef d1 #undef set_d0 #undef set_d1 static double ratio(Bigint *a, Bigint *b) { double da, db; int32 k, ka, kb; da = b2d(a, &ka); db = b2d(b, &kb); k = ka - kb + 32*(a->wds - b->wds); if (k > 0) set_word0(da, word0(da) + k*Exp_msk1); else { k = -k; set_word0(db, word0(db) + k*Exp_msk1); } return da / db; } static CONST double tens[] = { 1e0, 1e1, 1e2, 1e3, 1e4, 1e5, 1e6, 1e7, 1e8, 1e9, 1e10, 1e11, 1e12, 1e13, 1e14, 1e15, 1e16, 1e17, 1e18, 1e19, 1e20, 1e21, 1e22 }; static CONST double bigtens[] = { 1e16, 1e32, 1e64, 1e128, 1e256 }; static CONST double tinytens[] = { 1e-16, 1e-32, 1e-64, 1e-128, #ifdef Avoid_Underflow 9007199254740992.e-256 #else 1e-256 #endif }; /* The factor of 2^53 in tinytens[4] helps us avoid setting the underflow */ /* flag unnecessarily. It leads to a song and dance at the end of strtod. */ #define Scale_Bit 0x10 #define n_bigtens 5 #ifdef INFNAN_CHECK #ifndef NAN_WORD0 #define NAN_WORD0 0x7ff80000 #endif #ifndef NAN_WORD1 #define NAN_WORD1 0 #endif static int match(CONST char **sp, char *t) { int c, d; CONST char *s = *sp; while(d = *t++) { if ((c = *++s) >= 'A' && c <= 'Z') c += 'a' - 'A'; if (c != d) return 0; } *sp = s + 1; return 1; } #endif /* INFNAN_CHECK */ #ifdef JS_THREADSAFE static JSBool initialized = JS_FALSE; /* hacked replica of nspr _PR_InitDtoa */ static void InitDtoa(void) { freelist_lock = PR_NewLock(); p5s_lock = PR_NewLock(); initialized = JS_TRUE; } #endif void js_FinishDtoa(void) { int count; Bigint *temp; #ifdef JS_THREADSAFE if (initialized == JS_TRUE) { PR_DestroyLock(freelist_lock); PR_DestroyLock(p5s_lock); initialized = JS_FALSE; } #endif /* clear down the freelist array and p5s */ /* static Bigint *freelist[Kmax+1]; */ for (count = 0; count <= Kmax; count++) { Bigint **listp = &freelist[count]; while ((temp = *listp) != NULL) { *listp = temp->next; free(temp); } freelist[count] = NULL; } /* static Bigint *p5s; */ while (p5s) { temp = p5s; p5s = p5s->next; free(temp); } } /* nspr2 watcom bug ifdef omitted */ JS_FRIEND_API(double) JS_strtod(CONST char *s00, char **se, int *err) { int32 scale; int32 bb2, bb5, bbe, bd2, bd5, bbbits, bs2, c, dsign, e, e1, esign, i, j, k, nd, nd0, nf, nz, nz0, sign; CONST char *s, *s0, *s1; double aadj, aadj1, adj, rv, rv0; Long L; ULong y, z; Bigint *bb, *bb1, *bd, *bd0, *bs, *delta; *err = 0; bb = bd = bs = delta = NULL; sign = nz0 = nz = 0; rv = 0.; /* Locking for Balloc's shared buffers that will be used in this block */ ACQUIRE_DTOA_LOCK(); for(s = s00;;s++) switch(*s) { case '-': sign = 1; /* no break */ case '+': if (*++s) goto break2; /* no break */ case 0: s = s00; goto ret; case '\t': case '\n': case '\v': case '\f': case '\r': case ' ': continue; default: goto break2; } break2: if (*s == '0') { nz0 = 1; while(*++s == '0') ; if (!*s) goto ret; } s0 = s; y = z = 0; for(nd = nf = 0; (c = *s) >= '0' && c <= '9'; nd++, s++) if (nd < 9) y = 10*y + c - '0'; else if (nd < 16) z = 10*z + c - '0'; nd0 = nd; if (c == '.') { c = *++s; if (!nd) { for(; c == '0'; c = *++s) nz++; if (c > '0' && c <= '9') { s0 = s; nf += nz; nz = 0; goto have_dig; } goto dig_done; } for(; c >= '0' && c <= '9'; c = *++s) { have_dig: nz++; if (c -= '0') { nf += nz; for(i = 1; i < nz; i++) if (nd++ < 9) y *= 10; else if (nd <= DBL_DIG + 1) z *= 10; if (nd++ < 9) y = 10*y + c; else if (nd <= DBL_DIG + 1) z = 10*z + c; nz = 0; } } } dig_done: e = 0; if (c == 'e' || c == 'E') { if (!nd && !nz && !nz0) { s = s00; goto ret; } s00 = s; esign = 0; switch(c = *++s) { case '-': esign = 1; case '+': c = *++s; } if (c >= '0' && c <= '9') { while(c == '0') c = *++s; if (c > '0' && c <= '9') { L = c - '0'; s1 = s; while((c = *++s) >= '0' && c <= '9') L = 10*L + c - '0'; if (s - s1 > 8 || L > 19999) /* Avoid confusion from exponents * so large that e might overflow. */ e = 19999; /* safe for 16 bit ints */ else e = (int32)L; if (esign) e = -e; } else e = 0; } else s = s00; } if (!nd) { if (!nz && !nz0) { #ifdef INFNAN_CHECK /* Check for Nan and Infinity */ switch(c) { case 'i': case 'I': if (match(&s,"nfinity")) { set_word0(rv, 0x7ff00000); set_word1(rv, 0); goto ret; } break; case 'n': case 'N': if (match(&s, "an")) { set_word0(rv, NAN_WORD0); set_word1(rv, NAN_WORD1); goto ret; } } #endif /* INFNAN_CHECK */ s = s00; } goto ret; } e1 = e -= nf; /* Now we have nd0 digits, starting at s0, followed by a * decimal point, followed by nd-nd0 digits. The number we're * after is the integer represented by those digits times * 10**e */ if (!nd0) nd0 = nd; k = nd < DBL_DIG + 1 ? nd : DBL_DIG + 1; rv = y; if (k > 9) rv = tens[k - 9] * rv + z; bd0 = 0; if (nd <= DBL_DIG #ifndef RND_PRODQUOT && FLT_ROUNDS == 1 #endif ) { if (!e) goto ret; if (e > 0) { if (e <= Ten_pmax) { /* rv = */ rounded_product(rv, tens[e]); goto ret; } i = DBL_DIG - nd; if (e <= Ten_pmax + i) { /* A fancier test would sometimes let us do * this for larger i values. */ e -= i; rv *= tens[i]; /* rv = */ rounded_product(rv, tens[e]); goto ret; } } #ifndef Inaccurate_Divide else if (e >= -Ten_pmax) { /* rv = */ rounded_quotient(rv, tens[-e]); goto ret; } #endif } e1 += nd - k; scale = 0; /* Get starting approximation = rv * 10**e1 */ if (e1 > 0) { if ((i = e1 & 15) != 0) rv *= tens[i]; if (e1 &= ~15) { if (e1 > DBL_MAX_10_EXP) { ovfl: *err = JS_DTOA_ERANGE; #ifdef __STDC__ rv = HUGE_VAL; #else /* Can't trust HUGE_VAL */ set_word0(rv, Exp_mask); set_word1(rv, 0); #endif if (bd0) goto retfree; goto ret; } e1 >>= 4; for(j = 0; e1 > 1; j++, e1 >>= 1) if (e1 & 1) rv *= bigtens[j]; /* The last multiplication could overflow. */ set_word0(rv, word0(rv) - P*Exp_msk1); rv *= bigtens[j]; if ((z = word0(rv) & Exp_mask) > Exp_msk1*(DBL_MAX_EXP+Bias-P)) goto ovfl; if (z > Exp_msk1*(DBL_MAX_EXP+Bias-1-P)) { /* set to largest number */ /* (Can't trust DBL_MAX) */ set_word0(rv, Big0); set_word1(rv, Big1); } else set_word0(rv, word0(rv) + P*Exp_msk1); } } else if (e1 < 0) { e1 = -e1; if ((i = e1 & 15) != 0) rv /= tens[i]; if (e1 &= ~15) { e1 >>= 4; if (e1 >= 1 << n_bigtens) goto undfl; #ifdef Avoid_Underflow if (e1 & Scale_Bit) scale = P; for(j = 0; e1 > 0; j++, e1 >>= 1) if (e1 & 1) rv *= tinytens[j]; if (scale && (j = P + 1 - ((word0(rv) & Exp_mask) >> Exp_shift)) > 0) { /* scaled rv is denormal; zap j low bits */ if (j >= 32) { set_word1(rv, 0); set_word0(rv, word0(rv) & (0xffffffff << (j-32))); if (!word0(rv)) set_word0(rv, 1); } else set_word1(rv, word1(rv) & (0xffffffff << j)); } #else for(j = 0; e1 > 1; j++, e1 >>= 1) if (e1 & 1) rv *= tinytens[j]; /* The last multiplication could underflow. */ rv0 = rv; rv *= tinytens[j]; if (!rv) { rv = 2.*rv0; rv *= tinytens[j]; #endif if (!rv) { undfl: rv = 0.; *err = JS_DTOA_ERANGE; if (bd0) goto retfree; goto ret; } #ifndef Avoid_Underflow set_word0(rv, Tiny0); set_word1(rv, Tiny1); /* The refinement below will clean * this approximation up. */ } #endif } } /* Now the hard part -- adjusting rv to the correct value.*/ /* Put digits into bd: true value = bd * 10^e */ bd0 = s2b(s0, nd0, nd, y); if (!bd0) goto nomem; for(;;) { bd = Balloc(bd0->k); if (!bd) goto nomem; Bcopy(bd, bd0); bb = d2b(rv, &bbe, &bbbits); /* rv = bb * 2^bbe */ if (!bb) goto nomem; bs = i2b(1); if (!bs) goto nomem; if (e >= 0) { bb2 = bb5 = 0; bd2 = bd5 = e; } else { bb2 = bb5 = -e; bd2 = bd5 = 0; } if (bbe >= 0) bb2 += bbe; else bd2 -= bbe; bs2 = bb2; #ifdef Sudden_Underflow j = P + 1 - bbbits; #else #ifdef Avoid_Underflow j = bbe - scale; #else j = bbe; #endif i = j + bbbits - 1; /* logb(rv) */ if (i < Emin) /* denormal */ j += P - Emin; else j = P + 1 - bbbits; #endif bb2 += j; bd2 += j; #ifdef Avoid_Underflow bd2 += scale; #endif i = bb2 < bd2 ? bb2 : bd2; if (i > bs2) i = bs2; if (i > 0) { bb2 -= i; bd2 -= i; bs2 -= i; } if (bb5 > 0) { bs = pow5mult(bs, bb5); if (!bs) goto nomem; bb1 = mult(bs, bb); if (!bb1) goto nomem; Bfree(bb); bb = bb1; } if (bb2 > 0) { bb = lshift(bb, bb2); if (!bb) goto nomem; } if (bd5 > 0) { bd = pow5mult(bd, bd5); if (!bd) goto nomem; } if (bd2 > 0) { bd = lshift(bd, bd2); if (!bd) goto nomem; } if (bs2 > 0) { bs = lshift(bs, bs2); if (!bs) goto nomem; } delta = diff(bb, bd); if (!delta) goto nomem; dsign = delta->sign; delta->sign = 0; i = cmp(delta, bs); if (i < 0) { /* Error is less than half an ulp -- check for * special case of mantissa a power of two. */ if (dsign || word1(rv) || word0(rv) & Bndry_mask #ifdef Avoid_Underflow || (word0(rv) & Exp_mask) <= Exp_msk1 + P*Exp_msk1 #else || (word0(rv) & Exp_mask) <= Exp_msk1 #endif ) { #ifdef Avoid_Underflow if (!delta->x[0] && delta->wds == 1) dsign = 2; #endif break; } delta = lshift(delta,Log2P); if (!delta) goto nomem; if (cmp(delta, bs) > 0) goto drop_down; break; } if (i == 0) { /* exactly half-way between */ if (dsign) { if ((word0(rv) & Bndry_mask1) == Bndry_mask1 && word1(rv) == 0xffffffff) { /*boundary case -- increment exponent*/ set_word0(rv, (word0(rv) & Exp_mask) + Exp_msk1); set_word1(rv, 0); #ifdef Avoid_Underflow dsign = 0; #endif break; } } else if (!(word0(rv) & Bndry_mask) && !word1(rv)) { #ifdef Avoid_Underflow dsign = 2; #endif drop_down: /* boundary case -- decrement exponent */ #ifdef Sudden_Underflow L = word0(rv) & Exp_mask; if (L <= Exp_msk1) goto undfl; L -= Exp_msk1; #else L = (word0(rv) & Exp_mask) - Exp_msk1; #endif set_word0(rv, L | Bndry_mask1); set_word1(rv, 0xffffffff); break; } #ifndef ROUND_BIASED if (!(word1(rv) & LSB)) break; #endif if (dsign) rv += ulp(rv); #ifndef ROUND_BIASED else { rv -= ulp(rv); #ifndef Sudden_Underflow if (!rv) goto undfl; #endif } #ifdef Avoid_Underflow dsign = 1 - dsign; #endif #endif break; } if ((aadj = ratio(delta, bs)) <= 2.) { if (dsign) aadj = aadj1 = 1.; else if (word1(rv) || word0(rv) & Bndry_mask) { #ifndef Sudden_Underflow if (word1(rv) == Tiny1 && !word0(rv)) goto undfl; #endif aadj = 1.; aadj1 = -1.; } else { /* special case -- power of FLT_RADIX to be */ /* rounded down... */ if (aadj < 2./FLT_RADIX) aadj = 1./FLT_RADIX; else aadj *= 0.5; aadj1 = -aadj; } } else { aadj *= 0.5; aadj1 = dsign ? aadj : -aadj; #ifdef Check_FLT_ROUNDS switch(FLT_ROUNDS) { case 2: /* towards +infinity */ aadj1 -= 0.5; break; case 0: /* towards 0 */ case 3: /* towards -infinity */ aadj1 += 0.5; } #else if (FLT_ROUNDS == 0) aadj1 += 0.5; #endif } y = word0(rv) & Exp_mask; /* Check for overflow */ if (y == Exp_msk1*(DBL_MAX_EXP+Bias-1)) { rv0 = rv; set_word0(rv, word0(rv) - P*Exp_msk1); adj = aadj1 * ulp(rv); rv += adj; if ((word0(rv) & Exp_mask) >= Exp_msk1*(DBL_MAX_EXP+Bias-P)) { if (word0(rv0) == Big0 && word1(rv0) == Big1) goto ovfl; set_word0(rv, Big0); set_word1(rv, Big1); goto cont; } else set_word0(rv, word0(rv) + P*Exp_msk1); } else { #ifdef Sudden_Underflow if ((word0(rv) & Exp_mask) <= P*Exp_msk1) { rv0 = rv; set_word0(rv, word0(rv) + P*Exp_msk1); adj = aadj1 * ulp(rv); rv += adj; if ((word0(rv) & Exp_mask) <= P*Exp_msk1) { if (word0(rv0) == Tiny0 && word1(rv0) == Tiny1) goto undfl; set_word0(rv, Tiny0); set_word1(rv, Tiny1); goto cont; } else set_word0(rv, word0(rv) - P*Exp_msk1); } else { adj = aadj1 * ulp(rv); rv += adj; } #else /* Compute adj so that the IEEE rounding rules will * correctly round rv + adj in some half-way cases. * If rv * ulp(rv) is denormalized (i.e., * y <= (P-1)*Exp_msk1), we must adjust aadj to avoid * trouble from bits lost to denormalization; * example: 1.2e-307 . */ #ifdef Avoid_Underflow if (y <= P*Exp_msk1 && aadj > 1.) #else if (y <= (P-1)*Exp_msk1 && aadj > 1.) #endif { aadj1 = (double)(int32)(aadj + 0.5); if (!dsign) aadj1 = -aadj1; } #ifdef Avoid_Underflow if (scale && y <= P*Exp_msk1) set_word0(aadj1, word0(aadj1) + (P+1)*Exp_msk1 - y); #endif adj = aadj1 * ulp(rv); rv += adj; #endif } z = word0(rv) & Exp_mask; #ifdef Avoid_Underflow if (!scale) #endif if (y == z) { /* Can we stop now? */ L = (Long)aadj; aadj -= L; /* The tolerances below are conservative. */ if (dsign || word1(rv) || word0(rv) & Bndry_mask) { if (aadj < .4999999 || aadj > .5000001) break; } else if (aadj < .4999999/FLT_RADIX) break; } cont: Bfree(bb); Bfree(bd); Bfree(bs); Bfree(delta); bb = bd = bs = delta = NULL; } #ifdef Avoid_Underflow if (scale) { rv0 = 0.; set_word0(rv0, Exp_1 - P*Exp_msk1); set_word1(rv0, 0); if ((word0(rv) & Exp_mask) <= P*Exp_msk1 && word1(rv) & 1 && dsign != 2) { if (dsign) { #ifdef Sudden_Underflow /* rv will be 0, but this would give the */ /* right result if only rv *= rv0 worked. */ set_word0(rv, word0(rv) + P*Exp_msk1); set_word0(rv0, Exp_1 - 2*P*Exp_msk1); #endif rv += ulp(rv); } else set_word1(rv, word1(rv) & ~1); } rv *= rv0; } #endif /* Avoid_Underflow */ retfree: Bfree(bb); Bfree(bd); Bfree(bs); Bfree(bd0); Bfree(delta); ret: RELEASE_DTOA_LOCK(); if (se) *se = (char *)s; return sign ? -rv : rv; nomem: Bfree(bb); Bfree(bd); Bfree(bs); Bfree(bd0); Bfree(delta); RELEASE_DTOA_LOCK(); *err = JS_DTOA_ENOMEM; return 0; } /* Return floor(b/2^k) and set b to be the remainder. The returned quotient must be less than 2^32. */ static uint32 quorem2(Bigint *b, int32 k) { ULong mask; ULong result; ULong *bx, *bxe; int32 w; int32 n = k >> 5; k &= 0x1F; mask = (1<wds - n; if (w <= 0) return 0; JS_ASSERT(w <= 2); bx = b->x; bxe = bx + n; result = *bxe >> k; *bxe &= mask; if (w == 2) { JS_ASSERT(!(bxe[1] & ~mask)); if (k) result |= bxe[1] << (32 - k); } n++; while (!*bxe && bxe != bx) { n--; bxe--; } b->wds = n; return result; } /* Return floor(b/S) and set b to be the remainder. As added restrictions, b must not have * more words than S, the most significant word of S must not start with a 1 bit, and the * returned quotient must be less than 36. */ static int32 quorem(Bigint *b, Bigint *S) { int32 n; ULong *bx, *bxe, q, *sx, *sxe; #ifdef ULLong ULLong borrow, carry, y, ys; #else ULong borrow, carry, y, ys; ULong si, z, zs; #endif n = S->wds; JS_ASSERT(b->wds <= n); if (b->wds < n) return 0; sx = S->x; sxe = sx + --n; bx = b->x; bxe = bx + n; JS_ASSERT(*sxe <= 0x7FFFFFFF); q = *bxe / (*sxe + 1); /* ensure q <= true quotient */ JS_ASSERT(q < 36); if (q) { borrow = 0; carry = 0; do { #ifdef ULLong ys = *sx++ * (ULLong)q + carry; carry = ys >> 32; y = *bx - (ys & 0xffffffffUL) - borrow; borrow = y >> 32 & 1UL; *bx++ = (ULong)(y & 0xffffffffUL); #else si = *sx++; ys = (si & 0xffff) * q + carry; zs = (si >> 16) * q + (ys >> 16); carry = zs >> 16; y = (*bx & 0xffff) - (ys & 0xffff) - borrow; borrow = (y & 0x10000) >> 16; z = (*bx >> 16) - (zs & 0xffff) - borrow; borrow = (z & 0x10000) >> 16; Storeinc(bx, z, y); #endif } while(sx <= sxe); if (!*bxe) { bx = b->x; while(--bxe > bx && !*bxe) --n; b->wds = n; } } if (cmp(b, S) >= 0) { q++; borrow = 0; carry = 0; bx = b->x; sx = S->x; do { #ifdef ULLong ys = *sx++ + carry; carry = ys >> 32; y = *bx - (ys & 0xffffffffUL) - borrow; borrow = y >> 32 & 1UL; *bx++ = (ULong)(y & 0xffffffffUL); #else si = *sx++; ys = (si & 0xffff) + carry; zs = (si >> 16) + (ys >> 16); carry = zs >> 16; y = (*bx & 0xffff) - (ys & 0xffff) - borrow; borrow = (y & 0x10000) >> 16; z = (*bx >> 16) - (zs & 0xffff) - borrow; borrow = (z & 0x10000) >> 16; Storeinc(bx, z, y); #endif } while(sx <= sxe); bx = b->x; bxe = bx + n; if (!*bxe) { while(--bxe > bx && !*bxe) --n; b->wds = n; } } return (int32)q; } /* dtoa for IEEE arithmetic (dmg): convert double to ASCII string. * * Inspired by "How to Print Floating-Point Numbers Accurately" by * Guy L. Steele, Jr. and Jon L. White [Proc. ACM SIGPLAN '90, pp. 92-101]. * * Modifications: * 1. Rather than iterating, we use a simple numeric overestimate * to determine k = floor(log10(d)). We scale relevant * quantities using O(log2(k)) rather than O(k) multiplications. * 2. For some modes > 2 (corresponding to ecvt and fcvt), we don't * try to generate digits strictly left to right. Instead, we * compute with fewer bits and propagate the carry if necessary * when rounding the final digit up. This is often faster. * 3. Under the assumption that input will be rounded nearest, * mode 0 renders 1e23 as 1e23 rather than 9.999999999999999e22. * That is, we allow equality in stopping tests when the * round-nearest rule will give the same floating-point value * as would satisfaction of the stopping test with strict * inequality. * 4. We remove common factors of powers of 2 from relevant * quantities. * 5. When converting floating-point integers less than 1e16, * we use floating-point arithmetic rather than resorting * to multiple-precision integers. * 6. When asked to produce fewer than 15 digits, we first try * to get by with floating-point arithmetic; we resort to * multiple-precision integer arithmetic only if we cannot * guarantee that the floating-point calculation has given * the correctly rounded result. For k requested digits and * "uniformly" distributed input, the probability is * something like 10^(k-15) that we must resort to the Long * calculation. */ /* Always emits at least one digit. */ /* If biasUp is set, then rounding in modes 2 and 3 will round away from zero * when the number is exactly halfway between two representable values. For example, * rounding 2.5 to zero digits after the decimal point will return 3 and not 2. * 2.49 will still round to 2, and 2.51 will still round to 3. */ /* bufsize should be at least 20 for modes 0 and 1. For the other modes, * bufsize should be two greater than the maximum number of output characters expected. */ static JSBool js_dtoa(double d, int mode, JSBool biasUp, int ndigits, int *decpt, int *sign, char **rve, char *buf, size_t bufsize) { /* Arguments ndigits, decpt, sign are similar to those of ecvt and fcvt; trailing zeros are suppressed from the returned string. If not null, *rve is set to point to the end of the return value. If d is +-Infinity or NaN, then *decpt is set to 9999. mode: 0 ==> shortest string that yields d when read in and rounded to nearest. 1 ==> like 0, but with Steele & White stopping rule; e.g. with IEEE P754 arithmetic , mode 0 gives 1e23 whereas mode 1 gives 9.999999999999999e22. 2 ==> max(1,ndigits) significant digits. This gives a return value similar to that of ecvt, except that trailing zeros are suppressed. 3 ==> through ndigits past the decimal point. This gives a return value similar to that from fcvt, except that trailing zeros are suppressed, and ndigits can be negative. 4-9 should give the same return values as 2-3, i.e., 4 <= mode <= 9 ==> same return as mode 2 + (mode & 1). These modes are mainly for debugging; often they run slower but sometimes faster than modes 2-3. 4,5,8,9 ==> left-to-right digit generation. 6-9 ==> don't try fast floating-point estimate (if applicable). Values of mode other than 0-9 are treated as mode 0. Sufficient space is allocated to the return value to hold the suppressed trailing zeros. */ int32 bbits, b2, b5, be, dig, i, ieps, ilim, ilim0, ilim1, j, j1, k, k0, k_check, leftright, m2, m5, s2, s5, spec_case, try_quick; Long L; #ifndef Sudden_Underflow int32 denorm; ULong x; #endif Bigint *b, *b1, *delta, *mlo, *mhi, *S; double d2, ds, eps; char *s; if (word0(d) & Sign_bit) { /* set sign for everything, including 0's and NaNs */ *sign = 1; set_word0(d, word0(d) & ~Sign_bit); /* clear sign bit */ } else *sign = 0; if ((word0(d) & Exp_mask) == Exp_mask) { /* Infinity or NaN */ *decpt = 9999; s = !word1(d) && !(word0(d) & Frac_mask) ? "Infinity" : "NaN"; if ((s[0] == 'I' && bufsize < 9) || (s[0] == 'N' && bufsize < 4)) { JS_ASSERT(JS_FALSE); /* JS_SetError(JS_BUFFER_OVERFLOW_ERROR, 0); */ return JS_FALSE; } strcpy(buf, s); if (rve) { *rve = buf[3] ? buf + 8 : buf + 3; JS_ASSERT(**rve == '\0'); } return JS_TRUE; } b = NULL; /* initialize for abort protection */ S = NULL; mlo = mhi = NULL; if (!d) { no_digits: *decpt = 1; if (bufsize < 2) { JS_ASSERT(JS_FALSE); /* JS_SetError(JS_BUFFER_OVERFLOW_ERROR, 0); */ return JS_FALSE; } buf[0] = '0'; buf[1] = '\0'; /* copy "0" to buffer */ if (rve) *rve = buf + 1; /* We might have jumped to "no_digits" from below, so we need * to be sure to free the potentially allocated Bigints to avoid * memory leaks. */ Bfree(b); Bfree(S); if (mlo != mhi) Bfree(mlo); Bfree(mhi); return JS_TRUE; } b = d2b(d, &be, &bbits); if (!b) goto nomem; #ifdef Sudden_Underflow i = (int32)(word0(d) >> Exp_shift1 & (Exp_mask>>Exp_shift1)); #else if ((i = (int32)(word0(d) >> Exp_shift1 & (Exp_mask>>Exp_shift1))) != 0) { #endif d2 = d; set_word0(d2, word0(d2) & Frac_mask1); set_word0(d2, word0(d2) | Exp_11); /* log(x) ~=~ log(1.5) + (x-1.5)/1.5 * log10(x) = log(x) / log(10) * ~=~ log(1.5)/log(10) + (x-1.5)/(1.5*log(10)) * log10(d) = (i-Bias)*log(2)/log(10) + log10(d2) * * This suggests computing an approximation k to log10(d) by * * k = (i - Bias)*0.301029995663981 * + ( (d2-1.5)*0.289529654602168 + 0.176091259055681 ); * * We want k to be too large rather than too small. * The error in the first-order Taylor series approximation * is in our favor, so we just round up the constant enough * to compensate for any error in the multiplication of * (i - Bias) by 0.301029995663981; since |i - Bias| <= 1077, * and 1077 * 0.30103 * 2^-52 ~=~ 7.2e-14, * adding 1e-13 to the constant term more than suffices. * Hence we adjust the constant term to 0.1760912590558. * (We could get a more accurate k by invoking log10, * but this is probably not worthwhile.) */ i -= Bias; #ifndef Sudden_Underflow denorm = 0; } else { /* d is denormalized */ i = bbits + be + (Bias + (P-1) - 1); x = i > 32 ? word0(d) << (64 - i) | word1(d) >> (i - 32) : word1(d) << (32 - i); d2 = x; set_word0(d2, word0(d2) - 31*Exp_msk1); /* adjust exponent */ i -= (Bias + (P-1) - 1) + 1; denorm = 1; } #endif /* At this point d = f*2^i, where 1 <= f < 2. d2 is an approximation of f. */ ds = (d2-1.5)*0.289529654602168 + 0.1760912590558 + i*0.301029995663981; k = (int32)ds; if (ds < 0. && ds != k) k--; /* want k = floor(ds) */ k_check = 1; if (k >= 0 && k <= Ten_pmax) { if (d < tens[k]) k--; k_check = 0; } /* At this point floor(log10(d)) <= k <= floor(log10(d))+1. If k_check is zero, we're guaranteed that k = floor(log10(d)). */ j = bbits - i - 1; /* At this point d = b/2^j, where b is an odd integer. */ if (j >= 0) { b2 = 0; s2 = j; } else { b2 = -j; s2 = 0; } if (k >= 0) { b5 = 0; s5 = k; s2 += k; } else { b2 -= k; b5 = -k; s5 = 0; } /* At this point d/10^k = (b * 2^b2 * 5^b5) / (2^s2 * 5^s5), where b is an odd integer, b2 >= 0, b5 >= 0, s2 >= 0, and s5 >= 0. */ if (mode < 0 || mode > 9) mode = 0; try_quick = 1; if (mode > 5) { mode -= 4; try_quick = 0; } leftright = 1; ilim = ilim1 = 0; switch(mode) { case 0: case 1: ilim = ilim1 = -1; i = 18; ndigits = 0; break; case 2: leftright = 0; /* no break */ case 4: if (ndigits <= 0) ndigits = 1; ilim = ilim1 = i = ndigits; break; case 3: leftright = 0; /* no break */ case 5: i = ndigits + k + 1; ilim = i; ilim1 = i - 1; if (i <= 0) i = 1; } /* ilim is the maximum number of significant digits we want, based on k and ndigits. */ /* ilim1 is the maximum number of significant digits we want, based on k and ndigits, when it turns out that k was computed too high by one. */ /* Ensure space for at least i+1 characters, including trailing null. */ if (bufsize <= (size_t)i) { Bfree(b); JS_ASSERT(JS_FALSE); return JS_FALSE; } s = buf; if (ilim >= 0 && ilim <= Quick_max && try_quick) { /* Try to get by with floating-point arithmetic. */ i = 0; d2 = d; k0 = k; ilim0 = ilim; ieps = 2; /* conservative */ /* Divide d by 10^k, keeping track of the roundoff error and avoiding overflows. */ if (k > 0) { ds = tens[k&0xf]; j = k >> 4; if (j & Bletch) { /* prevent overflows */ j &= Bletch - 1; d /= bigtens[n_bigtens-1]; ieps++; } for(; j; j >>= 1, i++) if (j & 1) { ieps++; ds *= bigtens[i]; } d /= ds; } else if ((j1 = -k) != 0) { d *= tens[j1 & 0xf]; for(j = j1 >> 4; j; j >>= 1, i++) if (j & 1) { ieps++; d *= bigtens[i]; } } /* Check that k was computed correctly. */ if (k_check && d < 1. && ilim > 0) { if (ilim1 <= 0) goto fast_failed; ilim = ilim1; k--; d *= 10.; ieps++; } /* eps bounds the cumulative error. */ eps = ieps*d + 7.; set_word0(eps, word0(eps) - (P-1)*Exp_msk1); if (ilim == 0) { S = mhi = 0; d -= 5.; if (d > eps) goto one_digit; if (d < -eps) goto no_digits; goto fast_failed; } #ifndef No_leftright if (leftright) { /* Use Steele & White method of only * generating digits needed. */ eps = 0.5/tens[ilim-1] - eps; for(i = 0;;) { L = (Long)d; d -= L; *s++ = '0' + (char)L; if (d < eps) goto ret1; if (1. - d < eps) goto bump_up; if (++i >= ilim) break; eps *= 10.; d *= 10.; } } else { #endif /* Generate ilim digits, then fix them up. */ eps *= tens[ilim-1]; for(i = 1;; i++, d *= 10.) { L = (Long)d; d -= L; *s++ = '0' + (char)L; if (i == ilim) { if (d > 0.5 + eps) goto bump_up; else if (d < 0.5 - eps) { while(*--s == '0') ; s++; goto ret1; } break; } } #ifndef No_leftright } #endif fast_failed: s = buf; d = d2; k = k0; ilim = ilim0; } /* Do we have a "small" integer? */ if (be >= 0 && k <= Int_max) { /* Yes. */ ds = tens[k]; if (ndigits < 0 && ilim <= 0) { S = mhi = 0; if (ilim < 0 || d < 5*ds || (!biasUp && d == 5*ds)) goto no_digits; goto one_digit; } /* Use true number of digits to limit looping. */ for(i = 1; i<=k+1; i++) { L = (Long) (d / ds); d -= L*ds; #ifdef Check_FLT_ROUNDS /* If FLT_ROUNDS == 2, L will usually be high by 1 */ if (d < 0) { L--; d += ds; } #endif *s++ = '0' + (char)L; if (i == ilim) { d += d; if ((d > ds) || (d == ds && (L & 1 || biasUp))) { bump_up: while(*--s == '9') if (s == buf) { k++; *s = '0'; break; } ++*s++; } break; } d *= 10.; } goto ret1; } m2 = b2; m5 = b5; if (leftright) { if (mode < 2) { i = #ifndef Sudden_Underflow denorm ? be + (Bias + (P-1) - 1 + 1) : #endif 1 + P - bbits; /* i is 1 plus the number of trailing zero bits in d's significand. Thus, (2^m2 * 5^m5) / (2^(s2+i) * 5^s5) = (1/2 lsb of d)/10^k. */ } else { j = ilim - 1; if (m5 >= j) m5 -= j; else { s5 += j -= m5; b5 += j; m5 = 0; } if ((i = ilim) < 0) { m2 -= i; i = 0; } /* (2^m2 * 5^m5) / (2^(s2+i) * 5^s5) = (1/2 * 10^(1-ilim))/10^k. */ } b2 += i; s2 += i; mhi = i2b(1); if (!mhi) goto nomem; /* (mhi * 2^m2 * 5^m5) / (2^s2 * 5^s5) = one-half of last printed (when mode >= 2) or input (when mode < 2) significant digit, divided by 10^k. */ } /* We still have d/10^k = (b * 2^b2 * 5^b5) / (2^s2 * 5^s5). Reduce common factors in b2, m2, and s2 without changing the equalities. */ if (m2 > 0 && s2 > 0) { i = m2 < s2 ? m2 : s2; b2 -= i; m2 -= i; s2 -= i; } /* Fold b5 into b and m5 into mhi. */ if (b5 > 0) { if (leftright) { if (m5 > 0) { mhi = pow5mult(mhi, m5); if (!mhi) goto nomem; b1 = mult(mhi, b); if (!b1) goto nomem; Bfree(b); b = b1; } if ((j = b5 - m5) != 0) { b = pow5mult(b, j); if (!b) goto nomem; } } else { b = pow5mult(b, b5); if (!b) goto nomem; } } /* Now we have d/10^k = (b * 2^b2) / (2^s2 * 5^s5) and (mhi * 2^m2) / (2^s2 * 5^s5) = one-half of last printed or input significant digit, divided by 10^k. */ S = i2b(1); if (!S) goto nomem; if (s5 > 0) { S = pow5mult(S, s5); if (!S) goto nomem; } /* Now we have d/10^k = (b * 2^b2) / (S * 2^s2) and (mhi * 2^m2) / (S * 2^s2) = one-half of last printed or input significant digit, divided by 10^k. */ /* Check for special case that d is a normalized power of 2. */ spec_case = 0; if (mode < 2) { if (!word1(d) && !(word0(d) & Bndry_mask) #ifndef Sudden_Underflow && word0(d) & (Exp_mask & Exp_mask << 1) #endif ) { /* The special case. Here we want to be within a quarter of the last input significant digit instead of one half of it when the decimal output string's value is less than d. */ b2 += Log2P; s2 += Log2P; spec_case = 1; } } /* Arrange for convenient computation of quotients: * shift left if necessary so divisor has 4 leading 0 bits. * * Perhaps we should just compute leading 28 bits of S once * and for all and pass them and a shift to quorem, so it * can do shifts and ors to compute the numerator for q. */ if ((i = ((s5 ? 32 - hi0bits(S->x[S->wds-1]) : 1) + s2) & 0x1f) != 0) i = 32 - i; /* i is the number of leading zero bits in the most significant word of S*2^s2. */ if (i > 4) { i -= 4; b2 += i; m2 += i; s2 += i; } else if (i < 4) { i += 28; b2 += i; m2 += i; s2 += i; } /* Now S*2^s2 has exactly four leading zero bits in its most significant word. */ if (b2 > 0) { b = lshift(b, b2); if (!b) goto nomem; } if (s2 > 0) { S = lshift(S, s2); if (!S) goto nomem; } /* Now we have d/10^k = b/S and (mhi * 2^m2) / S = maximum acceptable error, divided by 10^k. */ if (k_check) { if (cmp(b,S) < 0) { k--; b = multadd(b, 10, 0); /* we botched the k estimate */ if (!b) goto nomem; if (leftright) { mhi = multadd(mhi, 10, 0); if (!mhi) goto nomem; } ilim = ilim1; } } /* At this point 1 <= d/10^k = b/S < 10. */ if (ilim <= 0 && mode > 2) { /* We're doing fixed-mode output and d is less than the minimum nonzero output in this mode. Output either zero or the minimum nonzero output depending on which is closer to d. */ if (ilim < 0) goto no_digits; S = multadd(S,5,0); if (!S) goto nomem; i = cmp(b,S); if (i < 0 || (i == 0 && !biasUp)) { /* Always emit at least one digit. If the number appears to be zero using the current mode, then emit one '0' digit and set decpt to 1. */ /*no_digits: k = -1 - ndigits; goto ret; */ goto no_digits; } one_digit: *s++ = '1'; k++; goto ret; } if (leftright) { if (m2 > 0) { mhi = lshift(mhi, m2); if (!mhi) goto nomem; } /* Compute mlo -- check for special case * that d is a normalized power of 2. */ mlo = mhi; if (spec_case) { mhi = Balloc(mhi->k); if (!mhi) goto nomem; Bcopy(mhi, mlo); mhi = lshift(mhi, Log2P); if (!mhi) goto nomem; } /* mlo/S = maximum acceptable error, divided by 10^k, if the output is less than d. */ /* mhi/S = maximum acceptable error, divided by 10^k, if the output is greater than d. */ for(i = 1;;i++) { dig = quorem(b,S) + '0'; /* Do we yet have the shortest decimal string * that will round to d? */ j = cmp(b, mlo); /* j is b/S compared with mlo/S. */ delta = diff(S, mhi); if (!delta) goto nomem; j1 = delta->sign ? 1 : cmp(b, delta); Bfree(delta); /* j1 is b/S compared with 1 - mhi/S. */ #ifndef ROUND_BIASED if (j1 == 0 && !mode && !(word1(d) & 1)) { if (dig == '9') goto round_9_up; if (j > 0) dig++; *s++ = (char)dig; goto ret; } #endif if ((j < 0) || (j == 0 && !mode #ifndef ROUND_BIASED && !(word1(d) & 1) #endif )) { if (j1 > 0) { /* Either dig or dig+1 would work here as the least significant decimal digit. Use whichever would produce a decimal value closer to d. */ b = lshift(b, 1); if (!b) goto nomem; j1 = cmp(b, S); if (((j1 > 0) || (j1 == 0 && (dig & 1 || biasUp))) && (dig++ == '9')) goto round_9_up; } *s++ = (char)dig; goto ret; } if (j1 > 0) { if (dig == '9') { /* possible if i == 1 */ round_9_up: *s++ = '9'; goto roundoff; } *s++ = (char)dig + 1; goto ret; } *s++ = (char)dig; if (i == ilim) break; b = multadd(b, 10, 0); if (!b) goto nomem; if (mlo == mhi) { mlo = mhi = multadd(mhi, 10, 0); if (!mhi) goto nomem; } else { mlo = multadd(mlo, 10, 0); if (!mlo) goto nomem; mhi = multadd(mhi, 10, 0); if (!mhi) goto nomem; } } } else for(i = 1;; i++) { *s++ = (char)(dig = quorem(b,S) + '0'); if (i >= ilim) break; b = multadd(b, 10, 0); if (!b) goto nomem; } /* Round off last digit */ b = lshift(b, 1); if (!b) goto nomem; j = cmp(b, S); if ((j > 0) || (j == 0 && (dig & 1 || biasUp))) { roundoff: while(*--s == '9') if (s == buf) { k++; *s++ = '1'; goto ret; } ++*s++; } else { /* Strip trailing zeros */ while(*--s == '0') ; s++; } ret: Bfree(S); if (mhi) { if (mlo && mlo != mhi) Bfree(mlo); Bfree(mhi); } ret1: Bfree(b); JS_ASSERT(s < buf + bufsize); *s = '\0'; if (rve) *rve = s; *decpt = k + 1; return JS_TRUE; nomem: Bfree(S); if (mhi) { if (mlo && mlo != mhi) Bfree(mlo); Bfree(mhi); } Bfree(b); return JS_FALSE; } /* Mapping of JSDToStrMode -> js_dtoa mode */ static const int dtoaModes[] = { 0, /* DTOSTR_STANDARD */ 0, /* DTOSTR_STANDARD_EXPONENTIAL, */ 3, /* DTOSTR_FIXED, */ 2, /* DTOSTR_EXPONENTIAL, */ 2}; /* DTOSTR_PRECISION */ JS_FRIEND_API(char *) JS_dtostr(char *buffer, size_t bufferSize, JSDToStrMode mode, int precision, double d) { int decPt; /* Position of decimal point relative to first digit returned by js_dtoa */ int sign; /* Nonzero if the sign bit was set in d */ int nDigits; /* Number of significand digits returned by js_dtoa */ char *numBegin = buffer+2; /* Pointer to the digits returned by js_dtoa; the +2 leaves space for */ /* the sign and/or decimal point */ char *numEnd; /* Pointer past the digits returned by js_dtoa */ JSBool dtoaRet; JS_ASSERT(bufferSize >= (size_t)(mode <= DTOSTR_STANDARD_EXPONENTIAL ? DTOSTR_STANDARD_BUFFER_SIZE : DTOSTR_VARIABLE_BUFFER_SIZE(precision))); if (mode == DTOSTR_FIXED && (d >= 1e21 || d <= -1e21)) mode = DTOSTR_STANDARD; /* Change mode here rather than below because the buffer may not be large enough to hold a large integer. */ /* Locking for Balloc's shared buffers */ ACQUIRE_DTOA_LOCK(); dtoaRet = js_dtoa(d, dtoaModes[mode], mode >= DTOSTR_FIXED, precision, &decPt, &sign, &numEnd, numBegin, bufferSize-2); RELEASE_DTOA_LOCK(); if (!dtoaRet) return 0; nDigits = numEnd - numBegin; /* If Infinity, -Infinity, or NaN, return the string regardless of the mode. */ if (decPt != 9999) { JSBool exponentialNotation = JS_FALSE; int minNDigits = 0; /* Minimum number of significand digits required by mode and precision */ char *p; char *q; switch (mode) { case DTOSTR_STANDARD: if (decPt < -5 || decPt > 21) exponentialNotation = JS_TRUE; else minNDigits = decPt; break; case DTOSTR_FIXED: if (precision >= 0) minNDigits = decPt + precision; else minNDigits = decPt; break; case DTOSTR_EXPONENTIAL: JS_ASSERT(precision > 0); minNDigits = precision; /* Fall through */ case DTOSTR_STANDARD_EXPONENTIAL: exponentialNotation = JS_TRUE; break; case DTOSTR_PRECISION: JS_ASSERT(precision > 0); minNDigits = precision; if (decPt < -5 || decPt > precision) exponentialNotation = JS_TRUE; break; } /* If the number has fewer than minNDigits, pad it with zeros at the end */ if (nDigits < minNDigits) { p = numBegin + minNDigits; nDigits = minNDigits; do { *numEnd++ = '0'; } while (numEnd != p); *numEnd = '\0'; } if (exponentialNotation) { /* Insert a decimal point if more than one significand digit */ if (nDigits != 1) { numBegin--; numBegin[0] = numBegin[1]; numBegin[1] = '.'; } JS_snprintf(numEnd, bufferSize - (numEnd - buffer), "e%+d", decPt-1); } else if (decPt != nDigits) { /* Some kind of a fraction in fixed notation */ JS_ASSERT(decPt <= nDigits); if (decPt > 0) { /* dd...dd . dd...dd */ p = --numBegin; do { *p = p[1]; p++; } while (--decPt); *p = '.'; } else { /* 0 . 00...00dd...dd */ p = numEnd; numEnd += 1 - decPt; q = numEnd; JS_ASSERT(numEnd < buffer + bufferSize); *numEnd = '\0'; while (p != numBegin) *--q = *--p; for (p = numBegin + 1; p != q; p++) *p = '0'; *numBegin = '.'; *--numBegin = '0'; } } } /* If negative and neither -0.0 nor NaN, output a leading '-'. */ if (sign && !(word0(d) == Sign_bit && word1(d) == 0) && !((word0(d) & Exp_mask) == Exp_mask && (word1(d) || (word0(d) & Frac_mask)))) { *--numBegin = '-'; } return numBegin; } /* Let b = floor(b / divisor), and return the remainder. b must be nonnegative. * divisor must be between 1 and 65536. * This function cannot run out of memory. */ static uint32 divrem(Bigint *b, uint32 divisor) { int32 n = b->wds; uint32 remainder = 0; ULong *bx; ULong *bp; JS_ASSERT(divisor > 0 && divisor <= 65536); if (!n) return 0; /* b is zero */ bx = b->x; bp = bx + n; do { ULong a = *--bp; ULong dividend = remainder << 16 | a >> 16; ULong quotientHi = dividend / divisor; ULong quotientLo; remainder = dividend - quotientHi*divisor; JS_ASSERT(quotientHi <= 0xFFFF && remainder < divisor); dividend = remainder << 16 | (a & 0xFFFF); quotientLo = dividend / divisor; remainder = dividend - quotientLo*divisor; JS_ASSERT(quotientLo <= 0xFFFF && remainder < divisor); *bp = quotientHi << 16 | quotientLo; } while (bp != bx); /* Decrease the size of the number if its most significant word is now zero. */ if (bx[n-1] == 0) b->wds--; return remainder; } /* "-0.0000...(1073 zeros after decimal point)...0001\0" is the longest string that we could produce, * which occurs when printing -5e-324 in binary. We could compute a better estimate of the size of * the output string and malloc fewer bytes depending on d and base, but why bother? */ #define DTOBASESTR_BUFFER_SIZE 1078 #define BASEDIGIT(digit) ((char)(((digit) >= 10) ? 'a' - 10 + (digit) : '0' + (digit))) JS_FRIEND_API(char *) JS_dtobasestr(int base, double d) { char *buffer; /* The output string */ char *p; /* Pointer to current position in the buffer */ char *pInt; /* Pointer to the beginning of the integer part of the string */ char *q; uint32 digit; double di; /* d truncated to an integer */ double df; /* The fractional part of d */ JS_ASSERT(base >= 2 && base <= 36); buffer = (char*) malloc(DTOBASESTR_BUFFER_SIZE); if (buffer) { p = buffer; if (d < 0.0 #if defined(XP_WIN) || defined(XP_OS2) && !((word0(d) & Exp_mask) == Exp_mask && ((word0(d) & Frac_mask) || word1(d))) /* Visual C++ doesn't know how to compare against NaN */ #endif ) { *p++ = '-'; d = -d; } /* Check for Infinity and NaN */ if ((word0(d) & Exp_mask) == Exp_mask) { strcpy(p, !word1(d) && !(word0(d) & Frac_mask) ? "Infinity" : "NaN"); return buffer; } /* Locking for Balloc's shared buffers */ ACQUIRE_DTOA_LOCK(); /* Output the integer part of d with the digits in reverse order. */ pInt = p; di = fd_floor(d); if (di <= 4294967295.0) { uint32 n = (uint32)di; if (n) do { uint32 m = n / base; digit = n - m*base; n = m; JS_ASSERT(digit < (uint32)base); *p++ = BASEDIGIT(digit); } while (n); else *p++ = '0'; } else { int32 e; int32 bits; /* Number of significant bits in di; not used. */ Bigint *b = d2b(di, &e, &bits); if (!b) goto nomem1; b = lshift(b, e); if (!b) { nomem1: Bfree(b); RELEASE_DTOA_LOCK(); free(buffer); return NULL; } do { digit = divrem(b, base); JS_ASSERT(digit < (uint32)base); *p++ = BASEDIGIT(digit); } while (b->wds); Bfree(b); } /* Reverse the digits of the integer part of d. */ q = p-1; while (q > pInt) { char ch = *pInt; *pInt++ = *q; *q-- = ch; } df = d - di; if (df != 0.0) { /* We have a fraction. */ int32 e, bbits, s2, done; Bigint *b, *s, *mlo, *mhi; b = s = mlo = mhi = NULL; *p++ = '.'; b = d2b(df, &e, &bbits); if (!b) { nomem2: Bfree(b); Bfree(s); if (mlo != mhi) Bfree(mlo); Bfree(mhi); RELEASE_DTOA_LOCK(); free(buffer); return NULL; } JS_ASSERT(e < 0); /* At this point df = b * 2^e. e must be less than zero because 0 < df < 1. */ s2 = -(int32)(word0(d) >> Exp_shift1 & Exp_mask>>Exp_shift1); #ifndef Sudden_Underflow if (!s2) s2 = -1; #endif s2 += Bias + P; /* 1/2^s2 = (nextDouble(d) - d)/2 */ JS_ASSERT(-s2 < e); mlo = i2b(1); if (!mlo) goto nomem2; mhi = mlo; if (!word1(d) && !(word0(d) & Bndry_mask) #ifndef Sudden_Underflow && word0(d) & (Exp_mask & Exp_mask << 1) #endif ) { /* The special case. Here we want to be within a quarter of the last input significant digit instead of one half of it when the output string's value is less than d. */ s2 += Log2P; mhi = i2b(1< df = b/2^s2 > 0; * (d - prevDouble(d))/2 = mlo/2^s2; * (nextDouble(d) - d)/2 = mhi/2^s2. */ done = JS_FALSE; do { int32 j, j1; Bigint *delta; b = multadd(b, base, 0); if (!b) goto nomem2; digit = quorem2(b, s2); if (mlo == mhi) { mlo = mhi = multadd(mlo, base, 0); if (!mhi) goto nomem2; } else { mlo = multadd(mlo, base, 0); if (!mlo) goto nomem2; mhi = multadd(mhi, base, 0); if (!mhi) goto nomem2; } /* Do we yet have the shortest string that will round to d? */ j = cmp(b, mlo); /* j is b/2^s2 compared with mlo/2^s2. */ delta = diff(s, mhi); if (!delta) goto nomem2; j1 = delta->sign ? 1 : cmp(b, delta); Bfree(delta); /* j1 is b/2^s2 compared with 1 - mhi/2^s2. */ #ifndef ROUND_BIASED if (j1 == 0 && !(word1(d) & 1)) { if (j > 0) digit++; done = JS_TRUE; } else #endif if (j < 0 || (j == 0 #ifndef ROUND_BIASED && !(word1(d) & 1) #endif )) { if (j1 > 0) { /* Either dig or dig+1 would work here as the least significant digit. Use whichever would produce an output value closer to d. */ b = lshift(b, 1); if (!b) goto nomem2; j1 = cmp(b, s); if (j1 > 0) /* The even test (|| (j1 == 0 && (digit & 1))) is not here because it messes up odd base output * such as 3.5 in base 3. */ digit++; } done = JS_TRUE; } else if (j1 > 0) { digit++; done = JS_TRUE; } JS_ASSERT(digit < (uint32)base); *p++ = BASEDIGIT(digit); } while (!done); Bfree(b); Bfree(s); if (mlo != mhi) Bfree(mlo); Bfree(mhi); } JS_ASSERT(p < buffer + DTOBASESTR_BUFFER_SIZE); *p = '\0'; RELEASE_DTOA_LOCK(); } return buffer; } pacparser-1.4.5/src/spidermonkey/js/src/jsdtoa.h000066400000000000000000000134571464010763600216620ustar00rootroot00000000000000/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- * * ***** BEGIN LICENSE BLOCK ***** * Version: MPL 1.1/GPL 2.0/LGPL 2.1 * * The contents of this file are subject to the Mozilla Public License Version * 1.1 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * http://www.mozilla.org/MPL/ * * Software distributed under the License is distributed on an "AS IS" basis, * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License * for the specific language governing rights and limitations under the * License. * * The Original Code is Mozilla Communicator client code, released * March 31, 1998. * * The Initial Developer of the Original Code is * Netscape Communications Corporation. * Portions created by the Initial Developer are Copyright (C) 1998 * the Initial Developer. All Rights Reserved. * * Contributor(s): * * Alternatively, the contents of this file may be used under the terms of * either of the GNU General Public License Version 2 or later (the "GPL"), * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), * in which case the provisions of the GPL or the LGPL are applicable instead * of those above. If you wish to allow use of your version of this file only * under the terms of either the GPL or the LGPL, and not to allow others to * use your version of this file under the terms of the MPL, indicate your * decision by deleting the provisions above and replace them with the notice * and other provisions required by the GPL or the LGPL. If you do not delete * the provisions above, a recipient may use your version of this file under * the terms of any one of the MPL, the GPL or the LGPL. * * ***** END LICENSE BLOCK ***** */ #ifndef jsdtoa_h___ #define jsdtoa_h___ /* * Public interface to portable double-precision floating point to string * and back conversion package. */ #include "jscompat.h" JS_BEGIN_EXTERN_C /* * JS_strtod() returns as a double-precision floating-point number * the value represented by the character string pointed to by * s00. The string is scanned up to the first unrecognized * character. * If the value of se is not (char **)NULL, a pointer to * the character terminating the scan is returned in the location pointed * to by se. If no number can be formed, se is set to s00r, and * zero is returned. * * *err is set to zero on success; it's set to JS_DTOA_ERANGE on range * errors and JS_DTOA_ENOMEM on memory failure. */ #define JS_DTOA_ERANGE 1 #define JS_DTOA_ENOMEM 2 JS_FRIEND_API(double) JS_strtod(const char *s00, char **se, int *err); /* * Modes for converting floating-point numbers to strings. * * Some of the modes can round-trip; this means that if the number is converted to * a string using one of these mode and then converted back to a number, the result * will be identical to the original number (except that, due to ECMA, -0 will get converted * to +0). These round-trip modes return the minimum number of significand digits that * permit the round trip. * * Some of the modes take an integer parameter . */ /* NB: Keep this in sync with number_constants[]. */ typedef enum JSDToStrMode { DTOSTR_STANDARD, /* Either fixed or exponential format; round-trip */ DTOSTR_STANDARD_EXPONENTIAL, /* Always exponential format; round-trip */ DTOSTR_FIXED, /* Round to digits after the decimal point; exponential if number is large */ DTOSTR_EXPONENTIAL, /* Always exponential format; significant digits */ DTOSTR_PRECISION /* Either fixed or exponential format; significant digits */ } JSDToStrMode; /* Maximum number of characters (including trailing null) that a DTOSTR_STANDARD or DTOSTR_STANDARD_EXPONENTIAL * conversion can produce. This maximum is reached for a number like -0.0000012345678901234567. */ #define DTOSTR_STANDARD_BUFFER_SIZE 26 /* Maximum number of characters (including trailing null) that one of the other conversions * can produce. This maximum is reached for TO_FIXED, which can generate up to 21 digits before the decimal point. */ #define DTOSTR_VARIABLE_BUFFER_SIZE(precision) ((precision)+24 > DTOSTR_STANDARD_BUFFER_SIZE ? (precision)+24 : DTOSTR_STANDARD_BUFFER_SIZE) /* * Convert dval according to the given mode and return a pointer to the resulting ASCII string. * The result is held somewhere in buffer, but not necessarily at the beginning. The size of * buffer is given in bufferSize, and must be at least as large as given by the above macros. * * Return NULL if out of memory. */ JS_FRIEND_API(char *) JS_dtostr(char *buffer, size_t bufferSize, JSDToStrMode mode, int precision, double dval); /* * Convert d to a string in the given base. The integral part of d will be printed exactly * in that base, regardless of how large it is, because there is no exponential notation for non-base-ten * numbers. The fractional part will be rounded to as few digits as possible while still preserving * the round-trip property (analogous to that of printing decimal numbers). In other words, if one were * to read the resulting string in via a hypothetical base-number-reading routine that rounds to the nearest * IEEE double (and to an even significand if there are two equally near doubles), then the result would * equal d (except for -0.0, which converts to "0", and NaN, which is not equal to itself). * * Return NULL if out of memory. If the result is not NULL, it must be released via free(). */ JS_FRIEND_API(char *) JS_dtobasestr(int base, double d); /* * Clean up any persistent RAM allocated during the execution of DtoA * routines, and remove any locks that might have been created. */ extern void js_FinishDtoa(void); JS_END_EXTERN_C #endif /* jsdtoa_h___ */ pacparser-1.4.5/src/spidermonkey/js/src/jsemit.c000066400000000000000000007303241464010763600216630ustar00rootroot00000000000000/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- * vim: set ts=8 sw=4 et tw=78: * * ***** BEGIN LICENSE BLOCK ***** * Version: MPL 1.1/GPL 2.0/LGPL 2.1 * * The contents of this file are subject to the Mozilla Public License Version * 1.1 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * http://www.mozilla.org/MPL/ * * Software distributed under the License is distributed on an "AS IS" basis, * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License * for the specific language governing rights and limitations under the * License. * * The Original Code is Mozilla Communicator client code, released * March 31, 1998. * * The Initial Developer of the Original Code is * Netscape Communications Corporation. * Portions created by the Initial Developer are Copyright (C) 1998 * the Initial Developer. All Rights Reserved. * * Contributor(s): * * Alternatively, the contents of this file may be used under the terms of * either of the GNU General Public License Version 2 or later (the "GPL"), * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), * in which case the provisions of the GPL or the LGPL are applicable instead * of those above. If you wish to allow use of your version of this file only * under the terms of either the GPL or the LGPL, and not to allow others to * use your version of this file under the terms of the MPL, indicate your * decision by deleting the provisions above and replace them with the notice * and other provisions required by the GPL or the LGPL. If you do not delete * the provisions above, a recipient may use your version of this file under * the terms of any one of the MPL, the GPL or the LGPL. * * ***** END LICENSE BLOCK ***** */ /* * JS bytecode generation. */ #include "jsstddef.h" #ifdef HAVE_MEMORY_H #include #endif #include #include "jstypes.h" #include "jsarena.h" /* Added by JSIFY */ #include "jsutil.h" /* Added by JSIFY */ #include "jsbit.h" #include "jsprf.h" #include "jsapi.h" #include "jsatom.h" #include "jscntxt.h" #include "jsconfig.h" #include "jsemit.h" #include "jsfun.h" #include "jsnum.h" #include "jsopcode.h" #include "jsparse.h" #include "jsregexp.h" #include "jsscan.h" #include "jsscope.h" #include "jsscript.h" /* Allocation chunk counts, must be powers of two in general. */ #define BYTECODE_CHUNK 256 /* code allocation increment */ #define SRCNOTE_CHUNK 64 /* initial srcnote allocation increment */ #define TRYNOTE_CHUNK 64 /* trynote allocation increment */ /* Macros to compute byte sizes from typed element counts. */ #define BYTECODE_SIZE(n) ((n) * sizeof(jsbytecode)) #define SRCNOTE_SIZE(n) ((n) * sizeof(jssrcnote)) #define TRYNOTE_SIZE(n) ((n) * sizeof(JSTryNote)) JS_FRIEND_API(JSBool) js_InitCodeGenerator(JSContext *cx, JSCodeGenerator *cg, JSArenaPool *codePool, JSArenaPool *notePool, const char *filename, uintN lineno, JSPrincipals *principals) { memset(cg, 0, sizeof *cg); TREE_CONTEXT_INIT(&cg->treeContext); cg->treeContext.flags |= TCF_COMPILING; cg->codePool = codePool; cg->notePool = notePool; cg->codeMark = JS_ARENA_MARK(codePool); cg->noteMark = JS_ARENA_MARK(notePool); cg->tempMark = JS_ARENA_MARK(&cx->tempPool); cg->current = &cg->main; cg->filename = filename; cg->firstLine = cg->prolog.currentLine = cg->main.currentLine = lineno; cg->principals = principals; ATOM_LIST_INIT(&cg->atomList); cg->prolog.noteMask = cg->main.noteMask = SRCNOTE_CHUNK - 1; ATOM_LIST_INIT(&cg->constList); return JS_TRUE; } JS_FRIEND_API(void) js_FinishCodeGenerator(JSContext *cx, JSCodeGenerator *cg) { TREE_CONTEXT_FINISH(&cg->treeContext); JS_ARENA_RELEASE(cg->codePool, cg->codeMark); JS_ARENA_RELEASE(cg->notePool, cg->noteMark); JS_ARENA_RELEASE(&cx->tempPool, cg->tempMark); } static ptrdiff_t EmitCheck(JSContext *cx, JSCodeGenerator *cg, JSOp op, ptrdiff_t delta) { jsbytecode *base, *limit, *next; ptrdiff_t offset, length; size_t incr, size; base = CG_BASE(cg); next = CG_NEXT(cg); limit = CG_LIMIT(cg); offset = PTRDIFF(next, base, jsbytecode); if (next + delta > limit) { length = offset + delta; length = (length <= BYTECODE_CHUNK) ? BYTECODE_CHUNK : JS_BIT(JS_CeilingLog2(length)); incr = BYTECODE_SIZE(length); if (!base) { JS_ARENA_ALLOCATE_CAST(base, jsbytecode *, cg->codePool, incr); } else { size = BYTECODE_SIZE(PTRDIFF(limit, base, jsbytecode)); incr -= size; JS_ARENA_GROW_CAST(base, jsbytecode *, cg->codePool, size, incr); } if (!base) { JS_ReportOutOfMemory(cx); return -1; } CG_BASE(cg) = base; CG_LIMIT(cg) = base + length; CG_NEXT(cg) = base + offset; } return offset; } static void UpdateDepth(JSContext *cx, JSCodeGenerator *cg, ptrdiff_t target) { jsbytecode *pc; const JSCodeSpec *cs; intN nuses; pc = CG_CODE(cg, target); cs = &js_CodeSpec[pc[0]]; nuses = cs->nuses; if (nuses < 0) nuses = 2 + GET_ARGC(pc); /* stack: fun, this, [argc arguments] */ cg->stackDepth -= nuses; JS_ASSERT(cg->stackDepth >= 0); if (cg->stackDepth < 0) { char numBuf[12]; JS_snprintf(numBuf, sizeof numBuf, "%d", target); JS_ReportErrorFlagsAndNumber(cx, JSREPORT_WARNING, js_GetErrorMessage, NULL, JSMSG_STACK_UNDERFLOW, cg->filename ? cg->filename : "stdin", numBuf); } cg->stackDepth += cs->ndefs; if ((uintN)cg->stackDepth > cg->maxStackDepth) cg->maxStackDepth = cg->stackDepth; } ptrdiff_t js_Emit1(JSContext *cx, JSCodeGenerator *cg, JSOp op) { ptrdiff_t offset = EmitCheck(cx, cg, op, 1); if (offset >= 0) { *CG_NEXT(cg)++ = (jsbytecode)op; UpdateDepth(cx, cg, offset); } return offset; } ptrdiff_t js_Emit2(JSContext *cx, JSCodeGenerator *cg, JSOp op, jsbytecode op1) { ptrdiff_t offset = EmitCheck(cx, cg, op, 2); if (offset >= 0) { jsbytecode *next = CG_NEXT(cg); next[0] = (jsbytecode)op; next[1] = op1; CG_NEXT(cg) = next + 2; UpdateDepth(cx, cg, offset); } return offset; } ptrdiff_t js_Emit3(JSContext *cx, JSCodeGenerator *cg, JSOp op, jsbytecode op1, jsbytecode op2) { ptrdiff_t offset = EmitCheck(cx, cg, op, 3); if (offset >= 0) { jsbytecode *next = CG_NEXT(cg); next[0] = (jsbytecode)op; next[1] = op1; next[2] = op2; CG_NEXT(cg) = next + 3; UpdateDepth(cx, cg, offset); } return offset; } ptrdiff_t js_EmitN(JSContext *cx, JSCodeGenerator *cg, JSOp op, size_t extra) { ptrdiff_t length = 1 + (ptrdiff_t)extra; ptrdiff_t offset = EmitCheck(cx, cg, op, length); if (offset >= 0) { jsbytecode *next = CG_NEXT(cg); *next = (jsbytecode)op; memset(next + 1, 0, BYTECODE_SIZE(extra)); CG_NEXT(cg) = next + length; UpdateDepth(cx, cg, offset); } return offset; } /* XXX too many "... statement" L10N gaffes below -- fix via js.msg! */ const char js_with_statement_str[] = "with statement"; const char js_finally_block_str[] = "finally block"; const char js_script_str[] = "script"; static const char *statementName[] = { "label statement", /* LABEL */ "if statement", /* IF */ "else statement", /* ELSE */ "switch statement", /* SWITCH */ "block", /* BLOCK */ js_with_statement_str, /* WITH */ "catch block", /* CATCH */ "try block", /* TRY */ js_finally_block_str, /* FINALLY */ js_finally_block_str, /* SUBROUTINE */ "do loop", /* DO_LOOP */ "for loop", /* FOR_LOOP */ "for/in loop", /* FOR_IN_LOOP */ "while loop", /* WHILE_LOOP */ }; static const char * StatementName(JSCodeGenerator *cg) { if (!cg->treeContext.topStmt) return js_script_str; return statementName[cg->treeContext.topStmt->type]; } static void ReportStatementTooLarge(JSContext *cx, JSCodeGenerator *cg) { JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_NEED_DIET, StatementName(cg)); } /** Span-dependent instructions in JS bytecode consist of the jump (JOF_JUMP) and switch (JOF_LOOKUPSWITCH, JOF_TABLESWITCH) format opcodes, subdivided into unconditional (gotos and gosubs), and conditional jumps or branches (which pop a value, test it, and jump depending on its value). Most jumps have just one immediate operand, a signed offset from the jump opcode's pc to the target bytecode. The lookup and table switch opcodes may contain many jump offsets. Mozilla bug #80981 (http://bugzilla.mozilla.org/show_bug.cgi?id=80981) was fixed by adding extended "X" counterparts to the opcodes/formats (NB: X is suffixed to prefer JSOP_ORX thereby avoiding a JSOP_XOR name collision for the extended form of the JSOP_OR branch opcode). The unextended or short formats have 16-bit signed immediate offset operands, the extended or long formats have 32-bit signed immediates. The span-dependency problem consists of selecting as few long instructions as possible, or about as few -- since jumps can span other jumps, extending one jump may cause another to need to be extended. Most JS scripts are short, so need no extended jumps. We optimize for this case by generating short jumps until we know a long jump is needed. After that point, we keep generating short jumps, but each jump's 16-bit immediate offset operand is actually an unsigned index into cg->spanDeps, an array of JSSpanDep structs. Each struct tells the top offset in the script of the opcode, the "before" offset of the jump (which will be the same as top for simplex jumps, but which will index further into the bytecode array for a non-initial jump offset in a lookup or table switch), the after "offset" adjusted during span-dependent instruction selection (initially the same value as the "before" offset), and the jump target (more below). Since we generate cg->spanDeps lazily, from within js_SetJumpOffset, we must ensure that all bytecode generated so far can be inspected to discover where the jump offset immediate operands lie within CG_CODE(cg). But the bonus is that we generate span-dependency records sorted by their offsets, so we can binary-search when trying to find a JSSpanDep for a given bytecode offset, or the nearest JSSpanDep at or above a given pc. To avoid limiting scripts to 64K jumps, if the cg->spanDeps index overflows 65534, we store SPANDEP_INDEX_HUGE in the jump's immediate operand. This tells us that we need to binary-search for the cg->spanDeps entry by the jump opcode's bytecode offset (sd->before). Jump targets need to be maintained in a data structure that lets us look up an already-known target by its address (jumps may have a common target), and that also lets us update the addresses (script-relative, a.k.a. absolute offsets) of targets that come after a jump target (for when a jump below that target needs to be extended). We use an AVL tree, implemented using recursion, but with some tricky optimizations to its height-balancing code (see http://www.cmcrossroads.com/bradapp/ftp/src/libs/C++/AvlTrees.html). A final wrinkle: backpatch chains are linked by jump-to-jump offsets with positive sign, even though they link "backward" (i.e., toward lower bytecode address). We don't want to waste space and search time in the AVL tree for such temporary backpatch deltas, so we use a single-bit wildcard scheme to tag true JSJumpTarget pointers and encode untagged, signed (positive) deltas in JSSpanDep.target pointers, depending on whether the JSSpanDep has a known target, or is still awaiting backpatching. Note that backpatch chains would present a problem for BuildSpanDepTable, which inspects bytecode to build cg->spanDeps on demand, when the first short jump offset overflows. To solve this temporary problem, we emit a proxy bytecode (JSOP_BACKPATCH; JSOP_BACKPATCH_POP for branch ops) whose nuses/ndefs counts help keep the stack balanced, but whose opcode format distinguishes its backpatch delta immediate operand from a normal jump offset. */ static int BalanceJumpTargets(JSJumpTarget **jtp) { JSJumpTarget *jt, *jt2, *root; int dir, otherDir, heightChanged; JSBool doubleRotate; jt = *jtp; JS_ASSERT(jt->balance != 0); if (jt->balance < -1) { dir = JT_RIGHT; doubleRotate = (jt->kids[JT_LEFT]->balance > 0); } else if (jt->balance > 1) { dir = JT_LEFT; doubleRotate = (jt->kids[JT_RIGHT]->balance < 0); } else { return 0; } otherDir = JT_OTHER_DIR(dir); if (doubleRotate) { jt2 = jt->kids[otherDir]; *jtp = root = jt2->kids[dir]; jt->kids[otherDir] = root->kids[dir]; root->kids[dir] = jt; jt2->kids[dir] = root->kids[otherDir]; root->kids[otherDir] = jt2; heightChanged = 1; root->kids[JT_LEFT]->balance = -JS_MAX(root->balance, 0); root->kids[JT_RIGHT]->balance = -JS_MIN(root->balance, 0); root->balance = 0; } else { *jtp = root = jt->kids[otherDir]; jt->kids[otherDir] = root->kids[dir]; root->kids[dir] = jt; heightChanged = (root->balance != 0); jt->balance = -((dir == JT_LEFT) ? --root->balance : ++root->balance); } return heightChanged; } typedef struct AddJumpTargetArgs { JSContext *cx; JSCodeGenerator *cg; ptrdiff_t offset; JSJumpTarget *node; } AddJumpTargetArgs; static int AddJumpTarget(AddJumpTargetArgs *args, JSJumpTarget **jtp) { JSJumpTarget *jt; int balanceDelta; jt = *jtp; if (!jt) { JSCodeGenerator *cg = args->cg; jt = cg->jtFreeList; if (jt) { cg->jtFreeList = jt->kids[JT_LEFT]; } else { JS_ARENA_ALLOCATE_CAST(jt, JSJumpTarget *, &args->cx->tempPool, sizeof *jt); if (!jt) { JS_ReportOutOfMemory(args->cx); return 0; } } jt->offset = args->offset; jt->balance = 0; jt->kids[JT_LEFT] = jt->kids[JT_RIGHT] = NULL; cg->numJumpTargets++; args->node = jt; *jtp = jt; return 1; } if (jt->offset == args->offset) { args->node = jt; return 0; } if (args->offset < jt->offset) balanceDelta = -AddJumpTarget(args, &jt->kids[JT_LEFT]); else balanceDelta = AddJumpTarget(args, &jt->kids[JT_RIGHT]); if (!args->node) return 0; jt->balance += balanceDelta; return (balanceDelta && jt->balance) ? 1 - BalanceJumpTargets(jtp) : 0; } #ifdef DEBUG_brendan static int AVLCheck(JSJumpTarget *jt) { int lh, rh; if (!jt) return 0; JS_ASSERT(-1 <= jt->balance && jt->balance <= 1); lh = AVLCheck(jt->kids[JT_LEFT]); rh = AVLCheck(jt->kids[JT_RIGHT]); JS_ASSERT(jt->balance == rh - lh); return 1 + JS_MAX(lh, rh); } #endif static JSBool SetSpanDepTarget(JSContext *cx, JSCodeGenerator *cg, JSSpanDep *sd, ptrdiff_t off) { AddJumpTargetArgs args; if (off < JUMPX_OFFSET_MIN || JUMPX_OFFSET_MAX < off) { ReportStatementTooLarge(cx, cg); return JS_FALSE; } args.cx = cx; args.cg = cg; args.offset = sd->top + off; args.node = NULL; AddJumpTarget(&args, &cg->jumpTargets); if (!args.node) return JS_FALSE; #ifdef DEBUG_brendan AVLCheck(cg->jumpTargets); #endif SD_SET_TARGET(sd, args.node); return JS_TRUE; } #define SPANDEPS_MIN 256 #define SPANDEPS_SIZE(n) ((n) * sizeof(JSSpanDep)) #define SPANDEPS_SIZE_MIN SPANDEPS_SIZE(SPANDEPS_MIN) static JSBool AddSpanDep(JSContext *cx, JSCodeGenerator *cg, jsbytecode *pc, jsbytecode *pc2, ptrdiff_t off) { uintN index; JSSpanDep *sdbase, *sd; size_t size; index = cg->numSpanDeps; if (index + 1 == 0) { ReportStatementTooLarge(cx, cg); return JS_FALSE; } if ((index & (index - 1)) == 0 && (!(sdbase = cg->spanDeps) || index >= SPANDEPS_MIN)) { if (!sdbase) { size = SPANDEPS_SIZE_MIN; JS_ARENA_ALLOCATE_CAST(sdbase, JSSpanDep *, &cx->tempPool, size); } else { size = SPANDEPS_SIZE(index); JS_ARENA_GROW_CAST(sdbase, JSSpanDep *, &cx->tempPool, size, size); } if (!sdbase) return JS_FALSE; cg->spanDeps = sdbase; } cg->numSpanDeps = index + 1; sd = cg->spanDeps + index; sd->top = PTRDIFF(pc, CG_BASE(cg), jsbytecode); sd->offset = sd->before = PTRDIFF(pc2, CG_BASE(cg), jsbytecode); if (js_CodeSpec[*pc].format & JOF_BACKPATCH) { /* Jump offset will be backpatched if off is a non-zero "bpdelta". */ if (off != 0) { JS_ASSERT(off >= 1 + JUMP_OFFSET_LEN); if (off > BPDELTA_MAX) { ReportStatementTooLarge(cx, cg); return JS_FALSE; } } SD_SET_BPDELTA(sd, off); } else if (off == 0) { /* Jump offset will be patched directly, without backpatch chaining. */ SD_SET_TARGET(sd, NULL); } else { /* The jump offset in off is non-zero, therefore it's already known. */ if (!SetSpanDepTarget(cx, cg, sd, off)) return JS_FALSE; } if (index > SPANDEP_INDEX_MAX) index = SPANDEP_INDEX_HUGE; SET_SPANDEP_INDEX(pc2, index); return JS_TRUE; } static JSBool BuildSpanDepTable(JSContext *cx, JSCodeGenerator *cg) { jsbytecode *pc, *end; JSOp op; const JSCodeSpec *cs; ptrdiff_t len, off; pc = CG_BASE(cg) + cg->spanDepTodo; end = CG_NEXT(cg); while (pc < end) { op = (JSOp)*pc; cs = &js_CodeSpec[op]; len = (ptrdiff_t)cs->length; switch (cs->format & JOF_TYPEMASK) { case JOF_JUMP: off = GET_JUMP_OFFSET(pc); if (!AddSpanDep(cx, cg, pc, pc, off)) return JS_FALSE; break; case JOF_TABLESWITCH: { jsbytecode *pc2; jsint i, low, high; pc2 = pc; off = GET_JUMP_OFFSET(pc2); if (!AddSpanDep(cx, cg, pc, pc2, off)) return JS_FALSE; pc2 += JUMP_OFFSET_LEN; low = GET_JUMP_OFFSET(pc2); pc2 += JUMP_OFFSET_LEN; high = GET_JUMP_OFFSET(pc2); pc2 += JUMP_OFFSET_LEN; for (i = low; i <= high; i++) { off = GET_JUMP_OFFSET(pc2); if (!AddSpanDep(cx, cg, pc, pc2, off)) return JS_FALSE; pc2 += JUMP_OFFSET_LEN; } len = 1 + pc2 - pc; break; } case JOF_LOOKUPSWITCH: { jsbytecode *pc2; jsint npairs; pc2 = pc; off = GET_JUMP_OFFSET(pc2); if (!AddSpanDep(cx, cg, pc, pc2, off)) return JS_FALSE; pc2 += JUMP_OFFSET_LEN; npairs = (jsint) GET_ATOM_INDEX(pc2); pc2 += ATOM_INDEX_LEN; while (npairs) { pc2 += ATOM_INDEX_LEN; off = GET_JUMP_OFFSET(pc2); if (!AddSpanDep(cx, cg, pc, pc2, off)) return JS_FALSE; pc2 += JUMP_OFFSET_LEN; npairs--; } len = 1 + pc2 - pc; break; } } JS_ASSERT(len > 0); pc += len; } return JS_TRUE; } static JSSpanDep * GetSpanDep(JSCodeGenerator *cg, jsbytecode *pc) { uintN index; ptrdiff_t offset; int lo, hi, mid; JSSpanDep *sd; index = GET_SPANDEP_INDEX(pc); if (index != SPANDEP_INDEX_HUGE) return cg->spanDeps + index; offset = PTRDIFF(pc, CG_BASE(cg), jsbytecode); lo = 0; hi = cg->numSpanDeps - 1; while (lo <= hi) { mid = (lo + hi) / 2; sd = cg->spanDeps + mid; if (sd->before == offset) return sd; if (sd->before < offset) lo = mid + 1; else hi = mid - 1; } JS_ASSERT(0); return NULL; } static JSBool SetBackPatchDelta(JSContext *cx, JSCodeGenerator *cg, jsbytecode *pc, ptrdiff_t delta) { JSSpanDep *sd; JS_ASSERT(delta >= 1 + JUMP_OFFSET_LEN); if (!cg->spanDeps && delta < JUMP_OFFSET_MAX) { SET_JUMP_OFFSET(pc, delta); return JS_TRUE; } if (delta > BPDELTA_MAX) { ReportStatementTooLarge(cx, cg); return JS_FALSE; } if (!cg->spanDeps && !BuildSpanDepTable(cx, cg)) return JS_FALSE; sd = GetSpanDep(cg, pc); JS_ASSERT(SD_GET_BPDELTA(sd) == 0); SD_SET_BPDELTA(sd, delta); return JS_TRUE; } static void UpdateJumpTargets(JSJumpTarget *jt, ptrdiff_t pivot, ptrdiff_t delta) { if (jt->offset > pivot) { jt->offset += delta; if (jt->kids[JT_LEFT]) UpdateJumpTargets(jt->kids[JT_LEFT], pivot, delta); } if (jt->kids[JT_RIGHT]) UpdateJumpTargets(jt->kids[JT_RIGHT], pivot, delta); } static JSSpanDep * FindNearestSpanDep(JSCodeGenerator *cg, ptrdiff_t offset, int lo, JSSpanDep *guard) { int num, hi, mid; JSSpanDep *sdbase, *sd; num = cg->numSpanDeps; JS_ASSERT(num > 0); hi = num - 1; sdbase = cg->spanDeps; while (lo <= hi) { mid = (lo + hi) / 2; sd = sdbase + mid; if (sd->before == offset) return sd; if (sd->before < offset) lo = mid + 1; else hi = mid - 1; } if (lo == num) return guard; sd = sdbase + lo; JS_ASSERT(sd->before >= offset && (lo == 0 || sd[-1].before < offset)); return sd; } static void FreeJumpTargets(JSCodeGenerator *cg, JSJumpTarget *jt) { if (jt->kids[JT_LEFT]) FreeJumpTargets(cg, jt->kids[JT_LEFT]); if (jt->kids[JT_RIGHT]) FreeJumpTargets(cg, jt->kids[JT_RIGHT]); jt->kids[JT_LEFT] = cg->jtFreeList; cg->jtFreeList = jt; } static JSBool OptimizeSpanDeps(JSContext *cx, JSCodeGenerator *cg) { jsbytecode *pc, *oldpc, *base, *limit, *next; JSSpanDep *sd, *sd2, *sdbase, *sdlimit, *sdtop, guard; ptrdiff_t offset, growth, delta, top, pivot, span, length, target; JSBool done; JSOp op; uint32 type; size_t size, incr; jssrcnote *sn, *snlimit; JSSrcNoteSpec *spec; uintN i, n, noteIndex; JSTryNote *tn, *tnlimit; #ifdef DEBUG_brendan int passes = 0; #endif base = CG_BASE(cg); sdbase = cg->spanDeps; sdlimit = sdbase + cg->numSpanDeps; offset = CG_OFFSET(cg); growth = 0; do { done = JS_TRUE; delta = 0; top = pivot = -1; sdtop = NULL; pc = NULL; op = JSOP_NOP; type = 0; #ifdef DEBUG_brendan passes++; #endif for (sd = sdbase; sd < sdlimit; sd++) { JS_ASSERT(JT_HAS_TAG(sd->target)); sd->offset += delta; if (sd->top != top) { sdtop = sd; top = sd->top; JS_ASSERT(top == sd->before); pivot = sd->offset; pc = base + top; op = (JSOp) *pc; type = (js_CodeSpec[op].format & JOF_TYPEMASK); if (JOF_TYPE_IS_EXTENDED_JUMP(type)) { /* * We already extended all the jump offset operands for * the opcode at sd->top. Jumps and branches have only * one jump offset operand, but switches have many, all * of which are adjacent in cg->spanDeps. */ continue; } JS_ASSERT(type == JOF_JUMP || type == JOF_TABLESWITCH || type == JOF_LOOKUPSWITCH); } if (!JOF_TYPE_IS_EXTENDED_JUMP(type)) { span = SD_SPAN(sd, pivot); if (span < JUMP_OFFSET_MIN || JUMP_OFFSET_MAX < span) { ptrdiff_t deltaFromTop = 0; done = JS_FALSE; switch (op) { case JSOP_GOTO: op = JSOP_GOTOX; break; case JSOP_IFEQ: op = JSOP_IFEQX; break; case JSOP_IFNE: op = JSOP_IFNEX; break; case JSOP_OR: op = JSOP_ORX; break; case JSOP_AND: op = JSOP_ANDX; break; case JSOP_GOSUB: op = JSOP_GOSUBX; break; case JSOP_CASE: op = JSOP_CASEX; break; case JSOP_DEFAULT: op = JSOP_DEFAULTX; break; case JSOP_TABLESWITCH: op = JSOP_TABLESWITCHX; break; case JSOP_LOOKUPSWITCH: op = JSOP_LOOKUPSWITCHX; break; default: ReportStatementTooLarge(cx, cg); return JS_FALSE; } *pc = (jsbytecode) op; for (sd2 = sdtop; sd2 < sdlimit && sd2->top == top; sd2++) { if (sd2 <= sd) { /* * sd2->offset already includes delta as it stood * before we entered this loop, but it must also * include the delta relative to top due to all the * extended jump offset immediates for the opcode * starting at top, which we extend in this loop. * * If there is only one extended jump offset, then * sd2->offset won't change and this for loop will * iterate once only. */ sd2->offset += deltaFromTop; deltaFromTop += JUMPX_OFFSET_LEN - JUMP_OFFSET_LEN; } else { /* * sd2 comes after sd, and won't be revisited by * the outer for loop, so we have to increase its * offset by delta, not merely by deltaFromTop. */ sd2->offset += delta; } delta += JUMPX_OFFSET_LEN - JUMP_OFFSET_LEN; UpdateJumpTargets(cg->jumpTargets, sd2->offset, JUMPX_OFFSET_LEN - JUMP_OFFSET_LEN); } sd = sd2 - 1; } } } growth += delta; } while (!done); if (growth) { #ifdef DEBUG_brendan printf("%s:%u: %u/%u jumps extended in %d passes (%d=%d+%d)\n", cg->filename ? cg->filename : "stdin", cg->firstLine, growth / (JUMPX_OFFSET_LEN - JUMP_OFFSET_LEN), cg->numSpanDeps, passes, offset + growth, offset, growth); #endif /* * Ensure that we have room for the extended jumps, but don't round up * to a power of two -- we're done generating code, so we cut to fit. */ limit = CG_LIMIT(cg); length = offset + growth; next = base + length; if (next > limit) { JS_ASSERT(length > BYTECODE_CHUNK); size = BYTECODE_SIZE(PTRDIFF(limit, base, jsbytecode)); incr = BYTECODE_SIZE(length) - size; JS_ARENA_GROW_CAST(base, jsbytecode *, cg->codePool, size, incr); if (!base) { JS_ReportOutOfMemory(cx); return JS_FALSE; } CG_BASE(cg) = base; CG_LIMIT(cg) = next = base + length; } CG_NEXT(cg) = next; /* * Set up a fake span dependency record to guard the end of the code * being generated. This guard record is returned as a fencepost by * FindNearestSpanDep if there is no real spandep at or above a given * unextended code offset. */ guard.top = -1; guard.offset = offset + growth; guard.before = offset; guard.target = NULL; } /* * Now work backwards through the span dependencies, copying chunks of * bytecode between each extended jump toward the end of the grown code * space, and restoring immediate offset operands for all jump bytecodes. * The first chunk of bytecodes, starting at base and ending at the first * extended jump offset (NB: this chunk includes the operation bytecode * just before that immediate jump offset), doesn't need to be copied. */ JS_ASSERT(sd == sdlimit); top = -1; while (--sd >= sdbase) { if (sd->top != top) { top = sd->top; op = (JSOp) base[top]; type = (js_CodeSpec[op].format & JOF_TYPEMASK); for (sd2 = sd - 1; sd2 >= sdbase && sd2->top == top; sd2--) continue; sd2++; pivot = sd2->offset; JS_ASSERT(top == sd2->before); } oldpc = base + sd->before; span = SD_SPAN(sd, pivot); /* * If this jump didn't need to be extended, restore its span immediate * offset operand now, overwriting the index of sd within cg->spanDeps * that was stored temporarily after *pc when BuildSpanDepTable ran. * * Note that span might fit in 16 bits even for an extended jump op, * if the op has multiple span operands, not all of which overflowed * (e.g. JSOP_LOOKUPSWITCH or JSOP_TABLESWITCH where some cases are in * range for a short jump, but others are not). */ if (!JOF_TYPE_IS_EXTENDED_JUMP(type)) { JS_ASSERT(JUMP_OFFSET_MIN <= span && span <= JUMP_OFFSET_MAX); SET_JUMP_OFFSET(oldpc, span); continue; } /* * Set up parameters needed to copy the next run of bytecode starting * at offset (which is a cursor into the unextended, original bytecode * vector), down to sd->before (a cursor of the same scale as offset, * it's the index of the original jump pc). Reuse delta to count the * nominal number of bytes to copy. */ pc = base + sd->offset; delta = offset - sd->before; JS_ASSERT(delta >= 1 + JUMP_OFFSET_LEN); /* * Don't bother copying the jump offset we're about to reset, but do * copy the bytecode at oldpc (which comes just before its immediate * jump offset operand), on the next iteration through the loop, by * including it in offset's new value. */ offset = sd->before + 1; size = BYTECODE_SIZE(delta - (1 + JUMP_OFFSET_LEN)); if (size) { memmove(pc + 1 + JUMPX_OFFSET_LEN, oldpc + 1 + JUMP_OFFSET_LEN, size); } SET_JUMPX_OFFSET(pc, span); } if (growth) { /* * Fix source note deltas. Don't hardwire the delta fixup adjustment, * even though currently it must be JUMPX_OFFSET_LEN - JUMP_OFFSET_LEN * at each sd that moved. The future may bring different offset sizes * for span-dependent instruction operands. However, we fix only main * notes here, not prolog notes -- we know that prolog opcodes are not * span-dependent, and aren't likely ever to be. */ offset = growth = 0; sd = sdbase; for (sn = cg->main.notes, snlimit = sn + cg->main.noteCount; sn < snlimit; sn = SN_NEXT(sn)) { /* * Recall that the offset of a given note includes its delta, and * tells the offset of the annotated bytecode from the main entry * point of the script. */ offset += SN_DELTA(sn); while (sd < sdlimit && sd->before < offset) { /* * To compute the delta to add to sn, we need to look at the * spandep after sd, whose offset - (before + growth) tells by * how many bytes sd's instruction grew. */ sd2 = sd + 1; if (sd2 == sdlimit) sd2 = &guard; delta = sd2->offset - (sd2->before + growth); if (delta > 0) { JS_ASSERT(delta == JUMPX_OFFSET_LEN - JUMP_OFFSET_LEN); sn = js_AddToSrcNoteDelta(cx, cg, sn, delta); if (!sn) return JS_FALSE; snlimit = cg->main.notes + cg->main.noteCount; growth += delta; } sd++; } /* * If sn has span-dependent offset operands, check whether each * covers further span-dependencies, and increase those operands * accordingly. Some source notes measure offset not from the * annotated pc, but from that pc plus some small bias. NB: we * assume that spec->offsetBias can't itself span span-dependent * instructions! */ spec = &js_SrcNoteSpec[SN_TYPE(sn)]; if (spec->isSpanDep) { pivot = offset + spec->offsetBias; n = spec->arity; for (i = 0; i < n; i++) { span = js_GetSrcNoteOffset(sn, i); if (span == 0) continue; target = pivot + span * spec->isSpanDep; sd2 = FindNearestSpanDep(cg, target, (target >= pivot) ? sd - sdbase : 0, &guard); /* * Increase target by sd2's before-vs-after offset delta, * which is absolute (i.e., relative to start of script, * as is target). Recompute the span by subtracting its * adjusted pivot from target. */ target += sd2->offset - sd2->before; span = target - (pivot + growth); span *= spec->isSpanDep; noteIndex = sn - cg->main.notes; if (!js_SetSrcNoteOffset(cx, cg, noteIndex, i, span)) return JS_FALSE; sn = cg->main.notes + noteIndex; snlimit = cg->main.notes + cg->main.noteCount; } } } cg->main.lastNoteOffset += growth; /* * Fix try/catch notes (O(numTryNotes * log2(numSpanDeps)), but it's * not clear how we can beat that). */ for (tn = cg->tryBase, tnlimit = cg->tryNext; tn < tnlimit; tn++) { /* * First, look for the nearest span dependency at/above tn->start. * There may not be any such spandep, in which case the guard will * be returned. */ offset = tn->start; sd = FindNearestSpanDep(cg, offset, 0, &guard); delta = sd->offset - sd->before; tn->start = offset + delta; /* * Next, find the nearest spandep at/above tn->start + tn->length. * Use its delta minus tn->start's delta to increase tn->length. */ length = tn->length; sd2 = FindNearestSpanDep(cg, offset + length, sd - sdbase, &guard); if (sd2 != sd) tn->length = length + sd2->offset - sd2->before - delta; /* * Finally, adjust tn->catchStart upward only if it is non-zero, * and provided there are spandeps below it that grew. */ offset = tn->catchStart; if (offset != 0) { sd = FindNearestSpanDep(cg, offset, sd2 - sdbase, &guard); tn->catchStart = offset + sd->offset - sd->before; } } } #ifdef DEBUG_brendan { uintN bigspans = 0; top = -1; for (sd = sdbase; sd < sdlimit; sd++) { offset = sd->offset; /* NB: sd->top cursors into the original, unextended bytecode vector. */ if (sd->top != top) { JS_ASSERT(top == -1 || !JOF_TYPE_IS_EXTENDED_JUMP(type) || bigspans != 0); bigspans = 0; top = sd->top; JS_ASSERT(top == sd->before); op = (JSOp) base[offset]; type = (js_CodeSpec[op].format & JOF_TYPEMASK); JS_ASSERT(type == JOF_JUMP || type == JOF_JUMPX || type == JOF_TABLESWITCH || type == JOF_TABLESWITCHX || type == JOF_LOOKUPSWITCH || type == JOF_LOOKUPSWITCHX); pivot = offset; } pc = base + offset; if (JOF_TYPE_IS_EXTENDED_JUMP(type)) { span = GET_JUMPX_OFFSET(pc); if (span < JUMP_OFFSET_MIN || JUMP_OFFSET_MAX < span) { bigspans++; } else { JS_ASSERT(type == JOF_TABLESWITCHX || type == JOF_LOOKUPSWITCHX); } } else { span = GET_JUMP_OFFSET(pc); } JS_ASSERT(SD_SPAN(sd, pivot) == span); } JS_ASSERT(!JOF_TYPE_IS_EXTENDED_JUMP(type) || bigspans != 0); } #endif /* * Reset so we optimize at most once -- cg may be used for further code * generation of successive, independent, top-level statements. No jump * can span top-level statements, because JS lacks goto. */ size = SPANDEPS_SIZE(JS_BIT(JS_CeilingLog2(cg->numSpanDeps))); JS_ArenaFreeAllocation(&cx->tempPool, cg->spanDeps, JS_MAX(size, SPANDEPS_SIZE_MIN)); cg->spanDeps = NULL; FreeJumpTargets(cg, cg->jumpTargets); cg->jumpTargets = NULL; cg->numSpanDeps = cg->numJumpTargets = 0; cg->spanDepTodo = CG_OFFSET(cg); return JS_TRUE; } static JSBool EmitJump(JSContext *cx, JSCodeGenerator *cg, JSOp op, ptrdiff_t off) { JSBool extend; ptrdiff_t jmp; jsbytecode *pc; extend = off < JUMP_OFFSET_MIN || JUMP_OFFSET_MAX < off; if (extend && !cg->spanDeps && !BuildSpanDepTable(cx, cg)) return JS_FALSE; jmp = js_Emit3(cx, cg, op, JUMP_OFFSET_HI(off), JUMP_OFFSET_LO(off)); if (jmp >= 0 && (extend || cg->spanDeps)) { pc = CG_CODE(cg, jmp); if (!AddSpanDep(cx, cg, pc, pc, off)) return JS_FALSE; } return jmp; } static ptrdiff_t GetJumpOffset(JSCodeGenerator *cg, jsbytecode *pc) { JSSpanDep *sd; JSJumpTarget *jt; ptrdiff_t top; if (!cg->spanDeps) return GET_JUMP_OFFSET(pc); sd = GetSpanDep(cg, pc); jt = sd->target; if (!JT_HAS_TAG(jt)) return JT_TO_BPDELTA(jt); top = sd->top; while (--sd >= cg->spanDeps && sd->top == top) continue; sd++; return JT_CLR_TAG(jt)->offset - sd->offset; } JSBool js_SetJumpOffset(JSContext *cx, JSCodeGenerator *cg, jsbytecode *pc, ptrdiff_t off) { if (!cg->spanDeps) { if (JUMP_OFFSET_MIN <= off && off <= JUMP_OFFSET_MAX) { SET_JUMP_OFFSET(pc, off); return JS_TRUE; } if (!BuildSpanDepTable(cx, cg)) return JS_FALSE; } return SetSpanDepTarget(cx, cg, GetSpanDep(cg, pc), off); } JSBool js_InStatement(JSTreeContext *tc, JSStmtType type) { JSStmtInfo *stmt; for (stmt = tc->topStmt; stmt; stmt = stmt->down) { if (stmt->type == type) return JS_TRUE; } return JS_FALSE; } JSBool js_IsGlobalReference(JSTreeContext *tc, JSAtom *atom, JSBool *loopyp) { JSStmtInfo *stmt; JSObject *obj; JSScope *scope; *loopyp = JS_FALSE; for (stmt = tc->topStmt; stmt; stmt = stmt->down) { if (stmt->type == STMT_WITH) return JS_FALSE; if (STMT_IS_LOOP(stmt)) { *loopyp = JS_TRUE; continue; } if (stmt->flags & SIF_SCOPE) { obj = ATOM_TO_OBJECT(stmt->atom); JS_ASSERT(LOCKED_OBJ_GET_CLASS(obj) == &js_BlockClass); scope = OBJ_SCOPE(obj); if (SCOPE_GET_PROPERTY(scope, ATOM_TO_JSID(atom))) return JS_FALSE; } } return JS_TRUE; } void js_PushStatement(JSTreeContext *tc, JSStmtInfo *stmt, JSStmtType type, ptrdiff_t top) { stmt->type = type; stmt->flags = 0; SET_STATEMENT_TOP(stmt, top); stmt->atom = NULL; stmt->down = tc->topStmt; tc->topStmt = stmt; if (STMT_LINKS_SCOPE(stmt)) { stmt->downScope = tc->topScopeStmt; tc->topScopeStmt = stmt; } else { stmt->downScope = NULL; } } void js_PushBlockScope(JSTreeContext *tc, JSStmtInfo *stmt, JSAtom *blockAtom, ptrdiff_t top) { JSObject *blockObj; js_PushStatement(tc, stmt, STMT_BLOCK, top); stmt->flags |= SIF_SCOPE; blockObj = ATOM_TO_OBJECT(blockAtom); blockObj->slots[JSSLOT_PARENT] = OBJECT_TO_JSVAL(tc->blockChain); stmt->downScope = tc->topScopeStmt; tc->topScopeStmt = stmt; tc->blockChain = blockObj; stmt->atom = blockAtom; } /* * Emit a backpatch op with offset pointing to the previous jump of this type, * so that we can walk back up the chain fixing up the op and jump offset. */ static ptrdiff_t EmitBackPatchOp(JSContext *cx, JSCodeGenerator *cg, JSOp op, ptrdiff_t *lastp) { ptrdiff_t offset, delta; offset = CG_OFFSET(cg); delta = offset - *lastp; *lastp = offset; JS_ASSERT(delta > 0); return EmitJump(cx, cg, op, delta); } /* * Macro to emit a bytecode followed by a uint16 immediate operand stored in * big-endian order, used for arg and var numbers as well as for atomIndexes. * NB: We use cx and cg from our caller's lexical environment, and return * false on error. */ #define EMIT_UINT16_IMM_OP(op, i) \ JS_BEGIN_MACRO \ if (js_Emit3(cx, cg, op, UINT16_HI(i), UINT16_LO(i)) < 0) \ return JS_FALSE; \ JS_END_MACRO /* Emit additional bytecode(s) for non-local jumps. */ static JSBool EmitNonLocalJumpFixup(JSContext *cx, JSCodeGenerator *cg, JSStmtInfo *toStmt, JSOp *returnop) { intN depth; JSStmtInfo *stmt; ptrdiff_t jmp; /* * Return from within a try block that has a finally clause must be split * into two ops: JSOP_SETRVAL, to pop the r.v. and store it in fp->rval; * and JSOP_RETRVAL, which makes control flow go back to the caller, who * picks up fp->rval as usual. Otherwise, the stack will be unbalanced * when executing the finally clause. * * We mutate *returnop once only if we find an enclosing try-block (viz, * STMT_FINALLY) to ensure that we emit just one JSOP_SETRVAL before one * or more JSOP_GOSUBs and other fixup opcodes emitted by this function. * Our caller (the TOK_RETURN case of js_EmitTree) then emits *returnop. * The fixup opcodes and gosubs must interleave in the proper order, from * inner statement to outer, so that finally clauses run at the correct * stack depth. */ if (returnop) { JS_ASSERT(*returnop == JSOP_RETURN); for (stmt = cg->treeContext.topStmt; stmt != toStmt; stmt = stmt->down) { if (stmt->type == STMT_FINALLY || ((cg->treeContext.flags & TCF_FUN_HEAVYWEIGHT) && STMT_MAYBE_SCOPE(stmt))) { if (js_Emit1(cx, cg, JSOP_SETRVAL) < 0) return JS_FALSE; *returnop = JSOP_RETRVAL; break; } } /* * If there are no try-with-finally blocks open around this return * statement, we can generate a return forthwith and skip generating * any fixup code. */ if (*returnop == JSOP_RETURN) return JS_TRUE; } /* * The non-local jump fixup we emit will unbalance cg->stackDepth, because * the fixup replicates balanced code such as JSOP_LEAVEWITH emitted at the * end of a with statement, so we save cg->stackDepth here and restore it * just before a successful return. */ depth = cg->stackDepth; for (stmt = cg->treeContext.topStmt; stmt != toStmt; stmt = stmt->down) { switch (stmt->type) { case STMT_FINALLY: if (js_NewSrcNote(cx, cg, SRC_HIDDEN) < 0) return JS_FALSE; jmp = EmitBackPatchOp(cx, cg, JSOP_BACKPATCH, &GOSUBS(*stmt)); if (jmp < 0) return JS_FALSE; break; case STMT_WITH: /* There's a With object on the stack that we need to pop. */ if (js_NewSrcNote(cx, cg, SRC_HIDDEN) < 0) return JS_FALSE; if (js_Emit1(cx, cg, JSOP_LEAVEWITH) < 0) return JS_FALSE; break; case STMT_FOR_IN_LOOP: /* * The iterator and the object being iterated need to be popped. */ if (js_NewSrcNote(cx, cg, SRC_HIDDEN) < 0) return JS_FALSE; if (js_Emit1(cx, cg, JSOP_ENDITER) < 0) return JS_FALSE; break; case STMT_SUBROUTINE: /* * There's a [exception or hole, retsub pc-index] pair on the * stack that we need to pop. */ if (js_NewSrcNote(cx, cg, SRC_HIDDEN) < 0) return JS_FALSE; if (js_Emit1(cx, cg, JSOP_POP2) < 0) return JS_FALSE; break; default:; } if (stmt->flags & SIF_SCOPE) { uintN i; /* There is a Block object with locals on the stack to pop. */ if (js_NewSrcNote(cx, cg, SRC_HIDDEN) < 0) return JS_FALSE; i = OBJ_BLOCK_COUNT(cx, ATOM_TO_OBJECT(stmt->atom)); EMIT_UINT16_IMM_OP(JSOP_LEAVEBLOCK, i); } } cg->stackDepth = depth; return JS_TRUE; } static ptrdiff_t EmitGoto(JSContext *cx, JSCodeGenerator *cg, JSStmtInfo *toStmt, ptrdiff_t *lastp, JSAtomListElement *label, JSSrcNoteType noteType) { intN index; if (!EmitNonLocalJumpFixup(cx, cg, toStmt, NULL)) return -1; if (label) index = js_NewSrcNote2(cx, cg, noteType, (ptrdiff_t) ALE_INDEX(label)); else if (noteType != SRC_NULL) index = js_NewSrcNote(cx, cg, noteType); else index = 0; if (index < 0) return -1; return EmitBackPatchOp(cx, cg, JSOP_BACKPATCH, lastp); } static JSBool BackPatch(JSContext *cx, JSCodeGenerator *cg, ptrdiff_t last, jsbytecode *target, jsbytecode op) { jsbytecode *pc, *stop; ptrdiff_t delta, span; pc = CG_CODE(cg, last); stop = CG_CODE(cg, -1); while (pc != stop) { delta = GetJumpOffset(cg, pc); span = PTRDIFF(target, pc, jsbytecode); CHECK_AND_SET_JUMP_OFFSET(cx, cg, pc, span); /* * Set *pc after jump offset in case bpdelta didn't overflow, but span * does (if so, CHECK_AND_SET_JUMP_OFFSET might call BuildSpanDepTable * and need to see the JSOP_BACKPATCH* op at *pc). */ *pc = op; pc -= delta; } return JS_TRUE; } void js_PopStatement(JSTreeContext *tc) { JSStmtInfo *stmt; JSObject *blockObj; stmt = tc->topStmt; tc->topStmt = stmt->down; if (STMT_LINKS_SCOPE(stmt)) { tc->topScopeStmt = stmt->downScope; if (stmt->flags & SIF_SCOPE) { blockObj = ATOM_TO_OBJECT(stmt->atom); tc->blockChain = JSVAL_TO_OBJECT(blockObj->slots[JSSLOT_PARENT]); } } } JSBool js_PopStatementCG(JSContext *cx, JSCodeGenerator *cg) { JSStmtInfo *stmt; stmt = cg->treeContext.topStmt; if (!STMT_IS_TRYING(stmt) && (!BackPatch(cx, cg, stmt->breaks, CG_NEXT(cg), JSOP_GOTO) || !BackPatch(cx, cg, stmt->continues, CG_CODE(cg, stmt->update), JSOP_GOTO))) { return JS_FALSE; } js_PopStatement(&cg->treeContext); return JS_TRUE; } JSBool js_DefineCompileTimeConstant(JSContext *cx, JSCodeGenerator *cg, JSAtom *atom, JSParseNode *pn) { jsdouble dval; jsint ival; JSAtom *valueAtom; JSAtomListElement *ale; /* XXX just do numbers for now */ if (pn->pn_type == TOK_NUMBER) { dval = pn->pn_dval; valueAtom = (JSDOUBLE_IS_INT(dval, ival) && INT_FITS_IN_JSVAL(ival)) ? js_AtomizeInt(cx, ival, 0) : js_AtomizeDouble(cx, dval, 0); if (!valueAtom) return JS_FALSE; ale = js_IndexAtom(cx, atom, &cg->constList); if (!ale) return JS_FALSE; ALE_SET_VALUE(ale, ATOM_KEY(valueAtom)); } return JS_TRUE; } JSStmtInfo * js_LexicalLookup(JSTreeContext *tc, JSAtom *atom, jsint *slotp, JSBool letdecl) { JSStmtInfo *stmt; JSObject *obj; JSScope *scope; JSScopeProperty *sprop; jsval v; for (stmt = tc->topScopeStmt; stmt; stmt = stmt->downScope) { if (stmt->type == STMT_WITH) { /* Ignore with statements enclosing a single let declaration. */ if (letdecl) continue; break; } /* Skip "maybe scope" statements that don't contain let bindings. */ if (!(stmt->flags & SIF_SCOPE)) continue; obj = ATOM_TO_OBJECT(stmt->atom); JS_ASSERT(LOCKED_OBJ_GET_CLASS(obj) == &js_BlockClass); scope = OBJ_SCOPE(obj); sprop = SCOPE_GET_PROPERTY(scope, ATOM_TO_JSID(atom)); if (sprop) { JS_ASSERT(sprop->flags & SPROP_HAS_SHORTID); if (slotp) { /* * Use LOCKED_OBJ_GET_SLOT since we know obj is single- * threaded and owned by this compiler activation. */ v = LOCKED_OBJ_GET_SLOT(obj, JSSLOT_BLOCK_DEPTH); JS_ASSERT(JSVAL_IS_INT(v) && JSVAL_TO_INT(v) >= 0); *slotp = JSVAL_TO_INT(v) + sprop->shortid; } return stmt; } } if (slotp) *slotp = -1; return stmt; } JSBool js_LookupCompileTimeConstant(JSContext *cx, JSCodeGenerator *cg, JSAtom *atom, jsval *vp) { JSBool ok; JSStackFrame *fp; JSStmtInfo *stmt; jsint slot; JSAtomListElement *ale; JSObject *obj, *pobj; JSProperty *prop; uintN attrs; /* * fp chases cg down the stack, but only until we reach the outermost cg. * This enables propagating consts from top-level into switch cases in a * function compiled along with the top-level script. All stack frames * with matching code generators should be flagged with JSFRAME_COMPILING; * we check sanity here. */ *vp = JSVAL_VOID; ok = JS_TRUE; fp = cx->fp; do { JS_ASSERT(fp->flags & JSFRAME_COMPILING); obj = fp->varobj; if (obj == fp->scopeChain) { /* XXX this will need revising when 'let const' is added. */ stmt = js_LexicalLookup(&cg->treeContext, atom, &slot, JS_FALSE); if (stmt) return JS_TRUE; ATOM_LIST_SEARCH(ale, &cg->constList, atom); if (ale) { *vp = ALE_VALUE(ale); return JS_TRUE; } /* * Try looking in the variable object for a direct property that * is readonly and permanent. We know such a property can't be * shadowed by another property on obj's prototype chain, or a * with object or catch variable; nor can prop's value be changed, * nor can prop be deleted. */ prop = NULL; if (OBJ_GET_CLASS(cx, obj) == &js_FunctionClass) { ok = js_LookupHiddenProperty(cx, obj, ATOM_TO_JSID(atom), &pobj, &prop); if (!ok) break; if (prop) { #ifdef DEBUG JSScopeProperty *sprop = (JSScopeProperty *)prop; /* * Any hidden property must be a formal arg or local var, * which will shadow a global const of the same name. */ JS_ASSERT(sprop->getter == js_GetArgument || sprop->getter == js_GetLocalVariable); #endif OBJ_DROP_PROPERTY(cx, pobj, prop); break; } } ok = OBJ_LOOKUP_PROPERTY(cx, obj, ATOM_TO_JSID(atom), &pobj, &prop); if (ok) { if (pobj == obj && (fp->flags & (JSFRAME_EVAL | JSFRAME_COMPILE_N_GO))) { /* * We're compiling code that will be executed immediately, * not re-executed against a different scope chain and/or * variable object. Therefore we can get constant values * from our variable object here. */ ok = OBJ_GET_ATTRIBUTES(cx, obj, ATOM_TO_JSID(atom), prop, &attrs); if (ok && !(~attrs & (JSPROP_READONLY | JSPROP_PERMANENT))) ok = OBJ_GET_PROPERTY(cx, obj, ATOM_TO_JSID(atom), vp); } if (prop) OBJ_DROP_PROPERTY(cx, pobj, prop); } if (!ok || prop) break; } fp = fp->down; } while ((cg = cg->parent) != NULL); return ok; } /* * Allocate an index invariant for all activations of the code being compiled * in cg, that can be used to store and fetch a reference to a cloned RegExp * object that shares the same JSRegExp private data created for the object * literal in pn->pn_atom. We need clones to hold lastIndex and other direct * properties that should not be shared among threads sharing a precompiled * function or script. * * If the code being compiled is function code, allocate a reserved slot in * the cloned function object that shares its precompiled script with other * cloned function objects and with the compiler-created clone-parent. There * are fun->nregexps such reserved slots in each function object cloned from * fun->object. NB: during compilation, funobj slots must never be allocated, * because js_AllocSlot could hand out one of the slots that should be given * to a regexp clone. * * If the code being compiled is global code, reserve the fp->vars slot at * ALE_INDEX(ale), by ensuring that cg->treeContext.numGlobalVars is at least * one more than this index. For global code, fp->vars is parallel to the * global script->atomMap.vector array, but possibly shorter for the common * case (where var declarations and regexp literals cluster toward the front * of the script or function body). * * Global variable name literals in script->atomMap have fast-global slot * numbers (stored as int-tagged jsvals) in the corresponding fp->vars array * element. The atomIndex for a regexp object literal thus also addresses an * fp->vars element that is not used by any optimized global variable, so we * use that GC-scanned element to keep the regexp object clone alive, as well * as to lazily create and find it at run-time for the JSOP_REGEXP bytecode. * * In no case can cx->fp->varobj be a Call object here, because that implies * we are compiling eval code, in which case (cx->fp->flags & JSFRAME_EVAL) * is true, and js_GetToken will have already selected JSOP_OBJECT instead of * JSOP_REGEXP, to avoid all this RegExp object cloning business. * * Why clone regexp objects? ECMA specifies that when a regular expression * literal is scanned, a RegExp object is created. In the spec, compilation * and execution happen indivisibly, but in this implementation and many of * its embeddings, code is precompiled early and re-executed in multiple * threads, or using multiple global objects, or both, for efficiency. * * In such cases, naively following ECMA leads to wrongful sharing of RegExp * objects, which makes for collisions on the lastIndex property (especially * for global regexps) and on any ad-hoc properties. Also, __proto__ and * __parent__ refer to the pre-compilation prototype and global objects, a * pigeon-hole problem for instanceof tests. */ static JSBool IndexRegExpClone(JSContext *cx, JSParseNode *pn, JSAtomListElement *ale, JSCodeGenerator *cg) { JSObject *varobj, *reobj; JSClass *clasp; JSFunction *fun; JSRegExp *re; uint16 *countPtr; uintN cloneIndex; JS_ASSERT(!(cx->fp->flags & (JSFRAME_EVAL | JSFRAME_COMPILE_N_GO))); varobj = cx->fp->varobj; clasp = OBJ_GET_CLASS(cx, varobj); if (clasp == &js_FunctionClass) { fun = (JSFunction *) JS_GetPrivate(cx, varobj); countPtr = &fun->u.i.nregexps; cloneIndex = *countPtr; } else { JS_ASSERT(clasp != &js_CallClass); countPtr = &cg->treeContext.numGlobalVars; cloneIndex = ALE_INDEX(ale); } if ((cloneIndex + 1) >> 16) { JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_NEED_DIET, js_script_str); return JS_FALSE; } if (cloneIndex >= *countPtr) *countPtr = cloneIndex + 1; reobj = ATOM_TO_OBJECT(pn->pn_atom); JS_ASSERT(OBJ_GET_CLASS(cx, reobj) == &js_RegExpClass); re = (JSRegExp *) JS_GetPrivate(cx, reobj); re->cloneIndex = cloneIndex; return JS_TRUE; } /* * Emit a bytecode and its 2-byte constant (atom) index immediate operand. * If the atomIndex requires more than 2 bytes, emit a prefix op whose 24-bit * immediate operand indexes the atom in script->atomMap. * * If op has JOF_NAME mode, emit JSOP_FINDNAME to find and push the object in * the scope chain in which the literal name was found, followed by the name * as a string. This enables us to use the JOF_ELEM counterpart to op. * * Otherwise, if op has JOF_PROP mode, emit JSOP_LITERAL before op, to push * the atom's value key. For JOF_PROP ops, the object being operated on has * already been pushed, and JSOP_LITERAL will push the id, leaving the stack * in the proper state for a JOF_ELEM counterpart. * * Otherwise, emit JSOP_LITOPX to push the atom index, then perform a special * dispatch on op, but getting op's atom index from the stack instead of from * an unsigned 16-bit immediate operand. */ static JSBool EmitAtomIndexOp(JSContext *cx, JSOp op, jsatomid atomIndex, JSCodeGenerator *cg) { uint32 mode; JSOp prefixOp; ptrdiff_t off; jsbytecode *pc; if (atomIndex >= JS_BIT(16)) { mode = (js_CodeSpec[op].format & JOF_MODEMASK); if (op != JSOP_SETNAME) { prefixOp = ((mode != JOF_NAME && mode != JOF_PROP) || #if JS_HAS_XML_SUPPORT op == JSOP_GETMETHOD || op == JSOP_SETMETHOD || #endif op == JSOP_SETCONST) ? JSOP_LITOPX : (mode == JOF_NAME) ? JSOP_FINDNAME : JSOP_LITERAL; off = js_EmitN(cx, cg, prefixOp, 3); if (off < 0) return JS_FALSE; pc = CG_CODE(cg, off); SET_LITERAL_INDEX(pc, atomIndex); } switch (op) { case JSOP_DECNAME: op = JSOP_DECELEM; break; case JSOP_DECPROP: op = JSOP_DECELEM; break; case JSOP_DELNAME: op = JSOP_DELELEM; break; case JSOP_DELPROP: op = JSOP_DELELEM; break; case JSOP_FORNAME: op = JSOP_FORELEM; break; case JSOP_FORPROP: op = JSOP_FORELEM; break; case JSOP_GETPROP: op = JSOP_GETELEM; break; case JSOP_GETXPROP: op = JSOP_GETXELEM; break; case JSOP_IMPORTPROP: op = JSOP_IMPORTELEM; break; case JSOP_INCNAME: op = JSOP_INCELEM; break; case JSOP_INCPROP: op = JSOP_INCELEM; break; case JSOP_INITPROP: op = JSOP_INITELEM; break; case JSOP_NAME: op = JSOP_GETELEM; break; case JSOP_NAMEDEC: op = JSOP_ELEMDEC; break; case JSOP_NAMEINC: op = JSOP_ELEMINC; break; case JSOP_PROPDEC: op = JSOP_ELEMDEC; break; case JSOP_PROPINC: op = JSOP_ELEMINC; break; case JSOP_BINDNAME: return JS_TRUE; case JSOP_SETNAME: op = JSOP_SETELEM; break; case JSOP_SETPROP: op = JSOP_SETELEM; break; #if JS_HAS_EXPORT_IMPORT case JSOP_EXPORTNAME: ReportStatementTooLarge(cx, cg); return JS_FALSE; #endif default: #if JS_HAS_XML_SUPPORT JS_ASSERT(mode == 0 || op == JSOP_SETCONST || op == JSOP_GETMETHOD || op == JSOP_SETMETHOD); #else JS_ASSERT(mode == 0 || op == JSOP_SETCONST); #endif break; } return js_Emit1(cx, cg, op) >= 0; } EMIT_UINT16_IMM_OP(op, atomIndex); return JS_TRUE; } /* * Slight sugar for EmitAtomIndexOp, again accessing cx and cg from the macro * caller's lexical environment, and embedding a false return on error. * XXXbe hey, who checks for fun->nvars and fun->nargs overflow?! */ #define EMIT_ATOM_INDEX_OP(op, atomIndex) \ JS_BEGIN_MACRO \ if (!EmitAtomIndexOp(cx, op, atomIndex, cg)) \ return JS_FALSE; \ JS_END_MACRO static JSBool EmitAtomOp(JSContext *cx, JSParseNode *pn, JSOp op, JSCodeGenerator *cg) { JSAtomListElement *ale; ale = js_IndexAtom(cx, pn->pn_atom, &cg->atomList); if (!ale) return JS_FALSE; if (op == JSOP_REGEXP && !IndexRegExpClone(cx, pn, ale, cg)) return JS_FALSE; return EmitAtomIndexOp(cx, op, ALE_INDEX(ale), cg); } /* * This routine tries to optimize name gets and sets to stack slot loads and * stores, given the variables object and scope chain in cx's top frame, the * compile-time context in tc, and a TOK_NAME node pn. It returns false on * error, true on success. * * The caller can inspect pn->pn_slot for a non-negative slot number to tell * whether optimization occurred, in which case BindNameToSlot also updated * pn->pn_op. If pn->pn_slot is still -1 on return, pn->pn_op nevertheless * may have been optimized, e.g., from JSOP_NAME to JSOP_ARGUMENTS. Whether * or not pn->pn_op was modified, if this function finds an argument or local * variable name, pn->pn_attrs will contain the property's attributes after a * successful return. * * NB: if you add more opcodes specialized from JSOP_NAME, etc., don't forget * to update the TOK_FOR (for-in) and TOK_ASSIGN (op=, e.g. +=) special cases * in js_EmitTree. */ static JSBool BindNameToSlot(JSContext *cx, JSTreeContext *tc, JSParseNode *pn, JSBool letdecl) { JSAtom *atom; JSStmtInfo *stmt; jsint slot; JSOp op; JSStackFrame *fp; JSObject *obj, *pobj; JSClass *clasp; JSBool optimizeGlobals; JSPropertyOp getter; uintN attrs; JSAtomListElement *ale; JSProperty *prop; JSScopeProperty *sprop; JS_ASSERT(pn->pn_type == TOK_NAME); if (pn->pn_slot >= 0 || pn->pn_op == JSOP_ARGUMENTS) return JS_TRUE; /* QNAME references can never be optimized to use arg/var storage. */ if (pn->pn_op == JSOP_QNAMEPART) return JS_TRUE; /* * We can't optimize if we are compiling a with statement and its body, * or we're in a catch block whose exception variable has the same name * as this node. FIXME: we should be able to optimize catch vars to be * block-locals. */ atom = pn->pn_atom; stmt = js_LexicalLookup(tc, atom, &slot, letdecl); if (stmt) { if (stmt->type == STMT_WITH) return JS_TRUE; JS_ASSERT(stmt->flags & SIF_SCOPE); JS_ASSERT(slot >= 0); op = pn->pn_op; switch (op) { case JSOP_NAME: op = JSOP_GETLOCAL; break; case JSOP_SETNAME: op = JSOP_SETLOCAL; break; case JSOP_INCNAME: op = JSOP_INCLOCAL; break; case JSOP_NAMEINC: op = JSOP_LOCALINC; break; case JSOP_DECNAME: op = JSOP_DECLOCAL; break; case JSOP_NAMEDEC: op = JSOP_LOCALDEC; break; case JSOP_FORNAME: op = JSOP_FORLOCAL; break; case JSOP_DELNAME: op = JSOP_FALSE; break; default: JS_ASSERT(0); } if (op != pn->pn_op) { pn->pn_op = op; pn->pn_slot = slot; } return JS_TRUE; } /* * A Script object can be used to split an eval into a compile step done * at construction time, and an execute step done separately, possibly in * a different scope altogether. We therefore cannot do any name-to-slot * optimizations, but must lookup names at runtime. Note that script_exec * ensures that its caller's frame has a Call object, so arg and var name * lookups will succeed. */ fp = cx->fp; if (fp->flags & JSFRAME_SCRIPT_OBJECT) return JS_TRUE; /* * We can't optimize if var and closure (a local function not in a larger * expression and not at top-level within another's body) collide. * XXX suboptimal: keep track of colliding names and deoptimize only those */ if (tc->flags & TCF_FUN_CLOSURE_VS_VAR) return JS_TRUE; /* * We can't optimize if we're not compiling a function body, whether via * eval, or directly when compiling a function statement or expression. */ obj = fp->varobj; clasp = OBJ_GET_CLASS(cx, obj); if (clasp != &js_FunctionClass && clasp != &js_CallClass) { /* Check for an eval or debugger frame. */ if (fp->flags & JSFRAME_SPECIAL) return JS_TRUE; /* * Optimize global variable accesses if there are at least 100 uses * in unambiguous contexts, or failing that, if least half of all the * uses of global vars/consts/functions are in loops. */ optimizeGlobals = (tc->globalUses >= 100 || (tc->loopyGlobalUses && tc->loopyGlobalUses >= tc->globalUses / 2)); if (!optimizeGlobals) return JS_TRUE; } else { optimizeGlobals = JS_FALSE; } /* * We can't optimize if we are in an eval called inside a with statement. */ if (fp->scopeChain != obj) return JS_TRUE; op = pn->pn_op; getter = NULL; #ifdef __GNUC__ attrs = slot = 0; /* quell GCC overwarning */ #endif if (optimizeGlobals) { /* * We are optimizing global variables, and there is no pre-existing * global property named atom. If atom was declared via const or var, * optimize pn to access fp->vars using the appropriate JOF_QVAR op. */ ATOM_LIST_SEARCH(ale, &tc->decls, atom); if (!ale) { /* Use precedes declaration, or name is never declared. */ return JS_TRUE; } attrs = (ALE_JSOP(ale) == JSOP_DEFCONST) ? JSPROP_ENUMERATE | JSPROP_READONLY | JSPROP_PERMANENT : JSPROP_ENUMERATE | JSPROP_PERMANENT; /* Index atom so we can map fast global number to name. */ JS_ASSERT(tc->flags & TCF_COMPILING); ale = js_IndexAtom(cx, atom, &((JSCodeGenerator *) tc)->atomList); if (!ale) return JS_FALSE; /* Defend against tc->numGlobalVars 16-bit overflow. */ slot = ALE_INDEX(ale); if ((slot + 1) >> 16) return JS_TRUE; if ((uint16)(slot + 1) > tc->numGlobalVars) tc->numGlobalVars = (uint16)(slot + 1); } else { /* * We may be able to optimize name to stack slot. Look for an argument * or variable property in the function, or its call object, not found * in any prototype object. Rewrite pn_op and update pn accordingly. * NB: We know that JSOP_DELNAME on an argument or variable evaluates * to false, due to JSPROP_PERMANENT. */ if (!js_LookupHiddenProperty(cx, obj, ATOM_TO_JSID(atom), &pobj, &prop)) return JS_FALSE; sprop = (JSScopeProperty *) prop; if (sprop) { if (pobj == obj) { getter = sprop->getter; attrs = sprop->attrs; slot = (sprop->flags & SPROP_HAS_SHORTID) ? sprop->shortid : -1; } OBJ_DROP_PROPERTY(cx, pobj, prop); } } if (optimizeGlobals || getter) { if (optimizeGlobals) { switch (op) { case JSOP_NAME: op = JSOP_GETGVAR; break; case JSOP_SETNAME: op = JSOP_SETGVAR; break; case JSOP_SETCONST: /* NB: no change */ break; case JSOP_INCNAME: op = JSOP_INCGVAR; break; case JSOP_NAMEINC: op = JSOP_GVARINC; break; case JSOP_DECNAME: op = JSOP_DECGVAR; break; case JSOP_NAMEDEC: op = JSOP_GVARDEC; break; case JSOP_FORNAME: /* NB: no change */ break; case JSOP_DELNAME: /* NB: no change */ break; default: JS_ASSERT(0); } } else if (getter == js_GetLocalVariable || getter == js_GetCallVariable) { switch (op) { case JSOP_NAME: op = JSOP_GETVAR; break; case JSOP_SETNAME: op = JSOP_SETVAR; break; case JSOP_SETCONST: op = JSOP_SETVAR; break; case JSOP_INCNAME: op = JSOP_INCVAR; break; case JSOP_NAMEINC: op = JSOP_VARINC; break; case JSOP_DECNAME: op = JSOP_DECVAR; break; case JSOP_NAMEDEC: op = JSOP_VARDEC; break; case JSOP_FORNAME: op = JSOP_FORVAR; break; case JSOP_DELNAME: op = JSOP_FALSE; break; default: JS_ASSERT(0); } } else if (getter == js_GetArgument || (getter == js_CallClass.getProperty && fp->fun && (uintN) slot < fp->fun->nargs)) { switch (op) { case JSOP_NAME: op = JSOP_GETARG; break; case JSOP_SETNAME: op = JSOP_SETARG; break; case JSOP_INCNAME: op = JSOP_INCARG; break; case JSOP_NAMEINC: op = JSOP_ARGINC; break; case JSOP_DECNAME: op = JSOP_DECARG; break; case JSOP_NAMEDEC: op = JSOP_ARGDEC; break; case JSOP_FORNAME: op = JSOP_FORARG; break; case JSOP_DELNAME: op = JSOP_FALSE; break; default: JS_ASSERT(0); } } if (op != pn->pn_op) { pn->pn_op = op; pn->pn_slot = slot; } pn->pn_attrs = attrs; } if (pn->pn_slot < 0) { /* * We couldn't optimize pn, so it's not a global or local slot name. * Now we must check for the predefined arguments variable. It may be * overridden by assignment, in which case the function is heavyweight * and the interpreter will look up 'arguments' in the function's call * object. */ if (pn->pn_op == JSOP_NAME && atom == cx->runtime->atomState.argumentsAtom) { pn->pn_op = JSOP_ARGUMENTS; return JS_TRUE; } tc->flags |= TCF_FUN_USES_NONLOCALS; } return JS_TRUE; } /* * If pn contains a useful expression, return true with *answer set to true. * If pn contains a useless expression, return true with *answer set to false. * Return false on error. * * The caller should initialize *answer to false and invoke this function on * an expression statement or similar subtree to decide whether the tree could * produce code that has any side effects. For an expression statement, we * define useless code as code with no side effects, because the main effect, * the value left on the stack after the code executes, will be discarded by a * pop bytecode. */ static JSBool CheckSideEffects(JSContext *cx, JSTreeContext *tc, JSParseNode *pn, JSBool *answer) { JSBool ok; JSFunction *fun; JSParseNode *pn2; ok = JS_TRUE; if (!pn || *answer) return ok; switch (pn->pn_arity) { case PN_FUNC: /* * A named function is presumed useful: we can't yet know that it is * not called. The side effects are the creation of a scope object * to parent this function object, and the binding of the function's * name in that scope object. See comments at case JSOP_NAMEDFUNOBJ: * in jsinterp.c. */ fun = (JSFunction *) JS_GetPrivate(cx, ATOM_TO_OBJECT(pn->pn_funAtom)); if (fun->atom) *answer = JS_TRUE; break; case PN_LIST: if (pn->pn_type == TOK_NEW || pn->pn_type == TOK_LP || pn->pn_type == TOK_LB || pn->pn_type == TOK_RB || pn->pn_type == TOK_RC) { /* * All invocation operations (construct: TOK_NEW, call: TOK_LP) * are presumed to be useful, because they may have side effects * even if their main effect (their return value) is discarded. * * TOK_LB binary trees of 3 or more nodes are flattened into lists * to avoid too much recursion. All such lists must be presumed * to be useful because each index operation could invoke a getter * (the JSOP_ARGUMENTS special case below, in the PN_BINARY case, * does not apply here: arguments[i][j] might invoke a getter). * * Array and object initializers (TOK_RB and TOK_RC lists) must be * considered useful, because they are sugar for constructor calls * (to Array and Object, respectively). */ *answer = JS_TRUE; } else { for (pn2 = pn->pn_head; pn2; pn2 = pn2->pn_next) ok &= CheckSideEffects(cx, tc, pn2, answer); } break; case PN_TERNARY: ok = CheckSideEffects(cx, tc, pn->pn_kid1, answer) && CheckSideEffects(cx, tc, pn->pn_kid2, answer) && CheckSideEffects(cx, tc, pn->pn_kid3, answer); break; case PN_BINARY: if (pn->pn_type == TOK_ASSIGN) { /* * Assignment is presumed to be useful, even if the next operation * is another assignment overwriting this one's ostensible effect, * because the left operand may be a property with a setter that * has side effects. * * The only exception is assignment of a useless value to a const * declared in the function currently being compiled. */ pn2 = pn->pn_left; if (pn2->pn_type != TOK_NAME) { *answer = JS_TRUE; } else { if (!BindNameToSlot(cx, tc, pn2, JS_FALSE)) return JS_FALSE; if (!CheckSideEffects(cx, tc, pn->pn_right, answer)) return JS_FALSE; if (!*answer && (pn2->pn_slot < 0 || !(pn2->pn_attrs & JSPROP_READONLY))) { *answer = JS_TRUE; } } } else { if (pn->pn_type == TOK_LB) { pn2 = pn->pn_left; if (pn2->pn_type == TOK_NAME && !BindNameToSlot(cx, tc, pn2, JS_FALSE)) { return JS_FALSE; } if (pn2->pn_op != JSOP_ARGUMENTS) { /* * Any indexed property reference could call a getter with * side effects, except for arguments[i] where arguments is * unambiguous. */ *answer = JS_TRUE; } } ok = CheckSideEffects(cx, tc, pn->pn_left, answer) && CheckSideEffects(cx, tc, pn->pn_right, answer); } break; case PN_UNARY: if (pn->pn_type == TOK_INC || pn->pn_type == TOK_DEC || pn->pn_type == TOK_THROW || #if JS_HAS_GENERATORS pn->pn_type == TOK_YIELD || #endif pn->pn_type == TOK_DEFSHARP) { /* All these operations have effects that we must commit. */ *answer = JS_TRUE; } else if (pn->pn_type == TOK_DELETE) { pn2 = pn->pn_kid; switch (pn2->pn_type) { case TOK_NAME: case TOK_DOT: #if JS_HAS_XML_SUPPORT case TOK_DBLDOT: #endif #if JS_HAS_LVALUE_RETURN case TOK_LP: #endif case TOK_LB: /* All these delete addressing modes have effects too. */ *answer = JS_TRUE; break; default: ok = CheckSideEffects(cx, tc, pn2, answer); break; } } else { ok = CheckSideEffects(cx, tc, pn->pn_kid, answer); } break; case PN_NAME: /* * Take care to avoid trying to bind a label name (labels, both for * statements and property values in object initialisers, have pn_op * defaulted to JSOP_NOP). */ if (pn->pn_type == TOK_NAME && pn->pn_op != JSOP_NOP) { if (!BindNameToSlot(cx, tc, pn, JS_FALSE)) return JS_FALSE; if (pn->pn_slot < 0 && pn->pn_op != JSOP_ARGUMENTS) { /* * Not an argument or local variable use, so this expression * could invoke a getter that has side effects. */ *answer = JS_TRUE; } } pn2 = pn->pn_expr; if (pn->pn_type == TOK_DOT) { if (pn2->pn_type == TOK_NAME && !BindNameToSlot(cx, tc, pn2, JS_FALSE)) { return JS_FALSE; } if (!(pn2->pn_op == JSOP_ARGUMENTS && pn->pn_atom == cx->runtime->atomState.lengthAtom)) { /* * Any dotted property reference could call a getter, except * for arguments.length where arguments is unambiguous. */ *answer = JS_TRUE; } } ok = CheckSideEffects(cx, tc, pn2, answer); break; case PN_NULLARY: if (pn->pn_type == TOK_DEBUGGER) *answer = JS_TRUE; break; } return ok; } /* * Secret handshake with js_EmitTree's TOK_LP/TOK_NEW case logic, to flag all * uses of JSOP_GETMETHOD that implicitly qualify the method property's name * with a function:: prefix. All other JSOP_GETMETHOD and JSOP_SETMETHOD uses * must be explicit, so we need a distinct source note (SRC_METHODBASE rather * than SRC_PCBASE) for round-tripping through the beloved decompiler. */ #define JSPROP_IMPLICIT_FUNCTION_NAMESPACE 0x100 static jssrcnote SrcNoteForPropOp(JSParseNode *pn, JSOp op) { return ((op == JSOP_GETMETHOD && !(pn->pn_attrs & JSPROP_IMPLICIT_FUNCTION_NAMESPACE)) || op == JSOP_SETMETHOD) ? SRC_METHODBASE : SRC_PCBASE; } static JSBool EmitPropOp(JSContext *cx, JSParseNode *pn, JSOp op, JSCodeGenerator *cg) { JSParseNode *pn2, *pndot, *pnup, *pndown; ptrdiff_t top; pn2 = pn->pn_expr; if (op == JSOP_GETPROP && pn->pn_type == TOK_DOT && pn2->pn_type == TOK_NAME) { /* Try to optimize arguments.length into JSOP_ARGCNT. */ if (!BindNameToSlot(cx, &cg->treeContext, pn2, JS_FALSE)) return JS_FALSE; if (pn2->pn_op == JSOP_ARGUMENTS && pn->pn_atom == cx->runtime->atomState.lengthAtom) { return js_Emit1(cx, cg, JSOP_ARGCNT) >= 0; } } /* * If the object operand is also a dotted property reference, reverse the * list linked via pn_expr temporarily so we can iterate over it from the * bottom up (reversing again as we go), to avoid excessive recursion. */ if (pn2->pn_type == TOK_DOT) { pndot = pn2; pnup = NULL; top = CG_OFFSET(cg); for (;;) { /* Reverse pndot->pn_expr to point up, not down. */ pndot->pn_offset = top; pndown = pndot->pn_expr; pndot->pn_expr = pnup; if (pndown->pn_type != TOK_DOT) break; pnup = pndot; pndot = pndown; } /* pndown is a primary expression, not a dotted property reference. */ if (!js_EmitTree(cx, cg, pndown)) return JS_FALSE; do { /* Walk back up the list, emitting annotated name ops. */ if (js_NewSrcNote2(cx, cg, SrcNoteForPropOp(pndot, pndot->pn_op), CG_OFFSET(cg) - pndown->pn_offset) < 0) { return JS_FALSE; } if (!EmitAtomOp(cx, pndot, pndot->pn_op, cg)) return JS_FALSE; /* Reverse the pn_expr link again. */ pnup = pndot->pn_expr; pndot->pn_expr = pndown; pndown = pndot; } while ((pndot = pnup) != NULL); } else { if (!js_EmitTree(cx, cg, pn2)) return JS_FALSE; } if (js_NewSrcNote2(cx, cg, SrcNoteForPropOp(pn, op), CG_OFFSET(cg) - pn2->pn_offset) < 0) { return JS_FALSE; } if (!pn->pn_atom) { JS_ASSERT(op == JSOP_IMPORTALL); if (js_Emit1(cx, cg, op) < 0) return JS_FALSE; } else { if (!EmitAtomOp(cx, pn, op, cg)) return JS_FALSE; } return JS_TRUE; } static JSBool EmitElemOp(JSContext *cx, JSParseNode *pn, JSOp op, JSCodeGenerator *cg) { ptrdiff_t top; JSParseNode *left, *right, *next, ltmp, rtmp; jsint slot; top = CG_OFFSET(cg); if (pn->pn_arity == PN_LIST) { /* Left-associative operator chain to avoid too much recursion. */ JS_ASSERT(pn->pn_op == JSOP_GETELEM || pn->pn_op == JSOP_IMPORTELEM); JS_ASSERT(pn->pn_count >= 3); left = pn->pn_head; right = PN_LAST(pn); next = left->pn_next; JS_ASSERT(next != right); /* * Try to optimize arguments[0][j]... into JSOP_ARGSUB<0> followed by * one or more index expression and JSOP_GETELEM op pairs. */ if (left->pn_type == TOK_NAME && next->pn_type == TOK_NUMBER) { if (!BindNameToSlot(cx, &cg->treeContext, left, JS_FALSE)) return JS_FALSE; if (left->pn_op == JSOP_ARGUMENTS && JSDOUBLE_IS_INT(next->pn_dval, slot) && (jsuint)slot < JS_BIT(16)) { left->pn_offset = next->pn_offset = top; EMIT_UINT16_IMM_OP(JSOP_ARGSUB, (jsatomid)slot); left = next; next = left->pn_next; } } /* * Check whether we generated JSOP_ARGSUB, just above, and have only * one more index expression to emit. Given arguments[0][j], we must * skip the while loop altogether, falling through to emit code for j * (in the subtree referenced by right), followed by the annotated op, * at the bottom of this function. */ JS_ASSERT(next != right || pn->pn_count == 3); if (left == pn->pn_head) { if (!js_EmitTree(cx, cg, left)) return JS_FALSE; } while (next != right) { if (!js_EmitTree(cx, cg, next)) return JS_FALSE; if (js_NewSrcNote2(cx, cg, SRC_PCBASE, CG_OFFSET(cg) - top) < 0) return JS_FALSE; if (js_Emit1(cx, cg, JSOP_GETELEM) < 0) return JS_FALSE; next = next->pn_next; } } else { if (pn->pn_arity == PN_NAME) { /* * Set left and right so pn appears to be a TOK_LB node, instead * of a TOK_DOT node. See the TOK_FOR/IN case in js_EmitTree, and * EmitDestructuringOps nearer below. In the destructuring case, * the base expression (pn_expr) of the name may be null, which * means we have to emit a JSOP_BINDNAME. */ left = pn->pn_expr; if (!left) { left = <mp; left->pn_type = TOK_OBJECT; left->pn_op = JSOP_BINDNAME; left->pn_arity = PN_NULLARY; left->pn_pos = pn->pn_pos; left->pn_atom = pn->pn_atom; } right = &rtmp; right->pn_type = TOK_STRING; JS_ASSERT(ATOM_IS_STRING(pn->pn_atom)); right->pn_op = js_IsIdentifier(ATOM_TO_STRING(pn->pn_atom)) ? JSOP_QNAMEPART : JSOP_STRING; right->pn_arity = PN_NULLARY; right->pn_pos = pn->pn_pos; right->pn_atom = pn->pn_atom; } else { JS_ASSERT(pn->pn_arity == PN_BINARY); left = pn->pn_left; right = pn->pn_right; } /* Try to optimize arguments[0] (e.g.) into JSOP_ARGSUB<0>. */ if (op == JSOP_GETELEM && left->pn_type == TOK_NAME && right->pn_type == TOK_NUMBER) { if (!BindNameToSlot(cx, &cg->treeContext, left, JS_FALSE)) return JS_FALSE; if (left->pn_op == JSOP_ARGUMENTS && JSDOUBLE_IS_INT(right->pn_dval, slot) && (jsuint)slot < JS_BIT(16)) { left->pn_offset = right->pn_offset = top; EMIT_UINT16_IMM_OP(JSOP_ARGSUB, (jsatomid)slot); return JS_TRUE; } } if (!js_EmitTree(cx, cg, left)) return JS_FALSE; } /* The right side of the descendant operator is implicitly quoted. */ JS_ASSERT(op != JSOP_DESCENDANTS || right->pn_type != TOK_STRING || right->pn_op == JSOP_QNAMEPART); if (!js_EmitTree(cx, cg, right)) return JS_FALSE; if (js_NewSrcNote2(cx, cg, SRC_PCBASE, CG_OFFSET(cg) - top) < 0) return JS_FALSE; return js_Emit1(cx, cg, op) >= 0; } static JSBool EmitNumberOp(JSContext *cx, jsdouble dval, JSCodeGenerator *cg) { jsint ival; jsatomid atomIndex; ptrdiff_t off; jsbytecode *pc; JSAtom *atom; JSAtomListElement *ale; if (JSDOUBLE_IS_INT(dval, ival) && INT_FITS_IN_JSVAL(ival)) { if (ival == 0) return js_Emit1(cx, cg, JSOP_ZERO) >= 0; if (ival == 1) return js_Emit1(cx, cg, JSOP_ONE) >= 0; atomIndex = (jsatomid)ival; if (atomIndex < JS_BIT(16)) { EMIT_UINT16_IMM_OP(JSOP_UINT16, atomIndex); return JS_TRUE; } if (atomIndex < JS_BIT(24)) { off = js_EmitN(cx, cg, JSOP_UINT24, 3); if (off < 0) return JS_FALSE; pc = CG_CODE(cg, off); SET_LITERAL_INDEX(pc, atomIndex); return JS_TRUE; } atom = js_AtomizeInt(cx, ival, 0); } else { atom = js_AtomizeDouble(cx, dval, 0); } if (!atom) return JS_FALSE; ale = js_IndexAtom(cx, atom, &cg->atomList); if (!ale) return JS_FALSE; return EmitAtomIndexOp(cx, JSOP_NUMBER, ALE_INDEX(ale), cg); } static JSBool EmitSwitch(JSContext *cx, JSCodeGenerator *cg, JSParseNode *pn, JSStmtInfo *stmtInfo) { JSOp switchOp; JSBool ok, hasDefault, constPropagated; ptrdiff_t top, off, defaultOffset; JSParseNode *pn2, *pn3, *pn4; uint32 caseCount, tableLength; JSParseNode **table; jsdouble d; jsint i, low, high; jsval v; JSAtom *atom; JSAtomListElement *ale; intN noteIndex; size_t switchSize, tableSize; jsbytecode *pc, *savepc; #if JS_HAS_BLOCK_SCOPE JSObject *obj; jsint count; #endif /* Try for most optimal, fall back if not dense ints, and per ECMAv2. */ switchOp = JSOP_TABLESWITCH; ok = JS_TRUE; hasDefault = constPropagated = JS_FALSE; defaultOffset = -1; /* * If the switch contains let variables scoped by its body, model the * resulting block on the stack first, before emitting the discriminant's * bytecode (in case the discriminant contains a stack-model dependency * such as a let expression). */ pn2 = pn->pn_right; #if JS_HAS_BLOCK_SCOPE if (pn2->pn_type == TOK_LEXICALSCOPE) { atom = pn2->pn_atom; obj = ATOM_TO_OBJECT(atom); OBJ_SET_BLOCK_DEPTH(cx, obj, cg->stackDepth); /* * Push the body's block scope before discriminant code-gen for proper * static block scope linkage in case the discriminant contains a let * expression. The block's locals must lie under the discriminant on * the stack so that case-dispatch bytecodes can find the discriminant * on top of stack. */ js_PushBlockScope(&cg->treeContext, stmtInfo, atom, -1); stmtInfo->type = STMT_SWITCH; count = OBJ_BLOCK_COUNT(cx, obj); cg->stackDepth += count; if ((uintN)cg->stackDepth > cg->maxStackDepth) cg->maxStackDepth = cg->stackDepth; /* Emit JSOP_ENTERBLOCK before code to evaluate the discriminant. */ ale = js_IndexAtom(cx, atom, &cg->atomList); if (!ale) return JS_FALSE; EMIT_ATOM_INDEX_OP(JSOP_ENTERBLOCK, ALE_INDEX(ale)); /* * Pop the switch's statement info around discriminant code-gen. Note * how this leaves cg->treeContext.blockChain referencing the switch's * block scope object, which is necessary for correct block parenting * in the case where the discriminant contains a let expression. */ cg->treeContext.topStmt = stmtInfo->down; cg->treeContext.topScopeStmt = stmtInfo->downScope; } #ifdef __GNUC__ else { atom = NULL; count = -1; } #endif #endif /* * Emit code for the discriminant first (or nearly first, in the case of a * switch whose body is a block scope). */ if (!js_EmitTree(cx, cg, pn->pn_left)) return JS_FALSE; /* Switch bytecodes run from here till end of final case. */ top = CG_OFFSET(cg); #if !JS_HAS_BLOCK_SCOPE js_PushStatement(&cg->treeContext, stmtInfo, STMT_SWITCH, top); #else if (pn2->pn_type == TOK_LC) { js_PushStatement(&cg->treeContext, stmtInfo, STMT_SWITCH, top); } else { /* Re-push the switch's statement info record. */ cg->treeContext.topStmt = cg->treeContext.topScopeStmt = stmtInfo; /* Set the statement info record's idea of top. */ stmtInfo->update = top; /* Advance pn2 to refer to the switch case list. */ pn2 = pn2->pn_expr; } #endif caseCount = pn2->pn_count; tableLength = 0; table = NULL; if (caseCount == 0 || (caseCount == 1 && (hasDefault = (pn2->pn_head->pn_type == TOK_DEFAULT)))) { caseCount = 0; low = 0; high = -1; } else { #define INTMAP_LENGTH 256 jsbitmap intmap_space[INTMAP_LENGTH]; jsbitmap *intmap = NULL; int32 intmap_bitlen = 0; low = JSVAL_INT_MAX; high = JSVAL_INT_MIN; for (pn3 = pn2->pn_head; pn3; pn3 = pn3->pn_next) { if (pn3->pn_type == TOK_DEFAULT) { hasDefault = JS_TRUE; caseCount--; /* one of the "cases" was the default */ continue; } JS_ASSERT(pn3->pn_type == TOK_CASE); if (switchOp == JSOP_CONDSWITCH) continue; pn4 = pn3->pn_left; switch (pn4->pn_type) { case TOK_NUMBER: d = pn4->pn_dval; if (JSDOUBLE_IS_INT(d, i) && INT_FITS_IN_JSVAL(i)) { pn3->pn_val = INT_TO_JSVAL(i); } else { atom = js_AtomizeDouble(cx, d, 0); if (!atom) { ok = JS_FALSE; goto release; } pn3->pn_val = ATOM_KEY(atom); } break; case TOK_STRING: pn3->pn_val = ATOM_KEY(pn4->pn_atom); break; case TOK_NAME: if (!pn4->pn_expr) { ok = js_LookupCompileTimeConstant(cx, cg, pn4->pn_atom, &v); if (!ok) goto release; if (!JSVAL_IS_VOID(v)) { pn3->pn_val = v; constPropagated = JS_TRUE; break; } } /* FALL THROUGH */ case TOK_PRIMARY: if (pn4->pn_op == JSOP_TRUE) { pn3->pn_val = JSVAL_TRUE; break; } if (pn4->pn_op == JSOP_FALSE) { pn3->pn_val = JSVAL_FALSE; break; } /* FALL THROUGH */ default: switchOp = JSOP_CONDSWITCH; continue; } JS_ASSERT(JSVAL_IS_NUMBER(pn3->pn_val) || JSVAL_IS_STRING(pn3->pn_val) || JSVAL_IS_BOOLEAN(pn3->pn_val)); if (switchOp != JSOP_TABLESWITCH) continue; if (!JSVAL_IS_INT(pn3->pn_val)) { switchOp = JSOP_LOOKUPSWITCH; continue; } i = JSVAL_TO_INT(pn3->pn_val); if ((jsuint)(i + (jsint)JS_BIT(15)) >= (jsuint)JS_BIT(16)) { switchOp = JSOP_LOOKUPSWITCH; continue; } if (i < low) low = i; if (high < i) high = i; /* * Check for duplicates, which require a JSOP_LOOKUPSWITCH. * We bias i by 65536 if it's negative, and hope that's a rare * case (because it requires a malloc'd bitmap). */ if (i < 0) i += JS_BIT(16); if (i >= intmap_bitlen) { if (!intmap && i < (INTMAP_LENGTH << JS_BITS_PER_WORD_LOG2)) { intmap = intmap_space; intmap_bitlen = INTMAP_LENGTH << JS_BITS_PER_WORD_LOG2; } else { /* Just grab 8K for the worst-case bitmap. */ intmap_bitlen = JS_BIT(16); intmap = (jsbitmap *) JS_malloc(cx, (JS_BIT(16) >> JS_BITS_PER_WORD_LOG2) * sizeof(jsbitmap)); if (!intmap) { JS_ReportOutOfMemory(cx); return JS_FALSE; } } memset(intmap, 0, intmap_bitlen >> JS_BITS_PER_BYTE_LOG2); } if (JS_TEST_BIT(intmap, i)) { switchOp = JSOP_LOOKUPSWITCH; continue; } JS_SET_BIT(intmap, i); } release: if (intmap && intmap != intmap_space) JS_free(cx, intmap); if (!ok) return JS_FALSE; /* * Compute table length and select lookup instead if overlarge or * more than half-sparse. */ if (switchOp == JSOP_TABLESWITCH) { tableLength = (uint32)(high - low + 1); if (tableLength >= JS_BIT(16) || tableLength > 2 * caseCount) switchOp = JSOP_LOOKUPSWITCH; } else if (switchOp == JSOP_LOOKUPSWITCH) { /* * Lookup switch supports only atom indexes below 64K limit. * Conservatively estimate the maximum possible index during * switch generation and use conditional switch if it exceeds * the limit. */ if (caseCount + cg->atomList.count > JS_BIT(16)) switchOp = JSOP_CONDSWITCH; } } /* * Emit a note with two offsets: first tells total switch code length, * second tells offset to first JSOP_CASE if condswitch. */ noteIndex = js_NewSrcNote3(cx, cg, SRC_SWITCH, 0, 0); if (noteIndex < 0) return JS_FALSE; if (switchOp == JSOP_CONDSWITCH) { /* * 0 bytes of immediate for unoptimized ECMAv2 switch. */ switchSize = 0; } else if (switchOp == JSOP_TABLESWITCH) { /* * 3 offsets (len, low, high) before the table, 1 per entry. */ switchSize = (size_t)(JUMP_OFFSET_LEN * (3 + tableLength)); } else { /* * JSOP_LOOKUPSWITCH: * 1 offset (len) and 1 atom index (npairs) before the table, * 1 atom index and 1 jump offset per entry. */ switchSize = (size_t)(JUMP_OFFSET_LEN + ATOM_INDEX_LEN + (ATOM_INDEX_LEN + JUMP_OFFSET_LEN) * caseCount); } /* * Emit switchOp followed by switchSize bytes of jump or lookup table. * * If switchOp is JSOP_LOOKUPSWITCH or JSOP_TABLESWITCH, it is crucial * to emit the immediate operand(s) by which bytecode readers such as * BuildSpanDepTable discover the length of the switch opcode *before* * calling js_SetJumpOffset (which may call BuildSpanDepTable). It's * also important to zero all unknown jump offset immediate operands, * so they can be converted to span dependencies with null targets to * be computed later (js_EmitN zeros switchSize bytes after switchOp). */ if (js_EmitN(cx, cg, switchOp, switchSize) < 0) return JS_FALSE; off = -1; if (switchOp == JSOP_CONDSWITCH) { intN caseNoteIndex = -1; JSBool beforeCases = JS_TRUE; /* Emit code for evaluating cases and jumping to case statements. */ for (pn3 = pn2->pn_head; pn3; pn3 = pn3->pn_next) { pn4 = pn3->pn_left; if (pn4 && !js_EmitTree(cx, cg, pn4)) return JS_FALSE; if (caseNoteIndex >= 0) { /* off is the previous JSOP_CASE's bytecode offset. */ if (!js_SetSrcNoteOffset(cx, cg, (uintN)caseNoteIndex, 0, CG_OFFSET(cg) - off)) { return JS_FALSE; } } if (!pn4) { JS_ASSERT(pn3->pn_type == TOK_DEFAULT); continue; } caseNoteIndex = js_NewSrcNote2(cx, cg, SRC_PCDELTA, 0); if (caseNoteIndex < 0) return JS_FALSE; off = EmitJump(cx, cg, JSOP_CASE, 0); if (off < 0) return JS_FALSE; pn3->pn_offset = off; if (beforeCases) { uintN noteCount, noteCountDelta; /* Switch note's second offset is to first JSOP_CASE. */ noteCount = CG_NOTE_COUNT(cg); if (!js_SetSrcNoteOffset(cx, cg, (uintN)noteIndex, 1, off - top)) { return JS_FALSE; } noteCountDelta = CG_NOTE_COUNT(cg) - noteCount; if (noteCountDelta != 0) caseNoteIndex += noteCountDelta; beforeCases = JS_FALSE; } } /* * If we didn't have an explicit default (which could fall in between * cases, preventing us from fusing this js_SetSrcNoteOffset with the * call in the loop above), link the last case to the implicit default * for the decompiler. */ if (!hasDefault && caseNoteIndex >= 0 && !js_SetSrcNoteOffset(cx, cg, (uintN)caseNoteIndex, 0, CG_OFFSET(cg) - off)) { return JS_FALSE; } /* Emit default even if no explicit default statement. */ defaultOffset = EmitJump(cx, cg, JSOP_DEFAULT, 0); if (defaultOffset < 0) return JS_FALSE; } else { pc = CG_CODE(cg, top + JUMP_OFFSET_LEN); if (switchOp == JSOP_TABLESWITCH) { /* Fill in switch bounds, which we know fit in 16-bit offsets. */ SET_JUMP_OFFSET(pc, low); pc += JUMP_OFFSET_LEN; SET_JUMP_OFFSET(pc, high); pc += JUMP_OFFSET_LEN; /* * Use malloc to avoid arena bloat for programs with many switches. * We free table if non-null at label out, so all control flow must * exit this function through goto out or goto bad. */ if (tableLength != 0) { tableSize = (size_t)tableLength * sizeof *table; table = (JSParseNode **) JS_malloc(cx, tableSize); if (!table) return JS_FALSE; memset(table, 0, tableSize); for (pn3 = pn2->pn_head; pn3; pn3 = pn3->pn_next) { if (pn3->pn_type == TOK_DEFAULT) continue; i = JSVAL_TO_INT(pn3->pn_val); i -= low; JS_ASSERT((uint32)i < tableLength); table[i] = pn3; } } } else { JS_ASSERT(switchOp == JSOP_LOOKUPSWITCH); /* Fill in the number of cases. */ SET_ATOM_INDEX(pc, caseCount); pc += ATOM_INDEX_LEN; } /* * After this point, all control flow involving JSOP_TABLESWITCH * must set ok and goto out to exit this function. To keep things * simple, all switchOp cases exit that way. */ if (constPropagated) { /* * Skip switchOp, as we are not setting jump offsets in the two * for loops below. We'll restore CG_NEXT(cg) from savepc after, * unless there was an error. */ savepc = CG_NEXT(cg); CG_NEXT(cg) = pc + 1; if (switchOp == JSOP_TABLESWITCH) { for (i = 0; i < (jsint)tableLength; i++) { pn3 = table[i]; if (pn3 && (pn4 = pn3->pn_left) != NULL && pn4->pn_type == TOK_NAME) { /* Note a propagated constant with the const's name. */ JS_ASSERT(!pn4->pn_expr); ale = js_IndexAtom(cx, pn4->pn_atom, &cg->atomList); if (!ale) goto bad; CG_NEXT(cg) = pc; if (js_NewSrcNote2(cx, cg, SRC_LABEL, (ptrdiff_t) ALE_INDEX(ale)) < 0) { goto bad; } } pc += JUMP_OFFSET_LEN; } } else { for (pn3 = pn2->pn_head; pn3; pn3 = pn3->pn_next) { pn4 = pn3->pn_left; if (pn4 && pn4->pn_type == TOK_NAME) { /* Note a propagated constant with the const's name. */ JS_ASSERT(!pn4->pn_expr); ale = js_IndexAtom(cx, pn4->pn_atom, &cg->atomList); if (!ale) goto bad; CG_NEXT(cg) = pc; if (js_NewSrcNote2(cx, cg, SRC_LABEL, (ptrdiff_t) ALE_INDEX(ale)) < 0) { goto bad; } } pc += ATOM_INDEX_LEN + JUMP_OFFSET_LEN; } } CG_NEXT(cg) = savepc; } } /* Emit code for each case's statements, copying pn_offset up to pn3. */ for (pn3 = pn2->pn_head; pn3; pn3 = pn3->pn_next) { if (switchOp == JSOP_CONDSWITCH && pn3->pn_type != TOK_DEFAULT) CHECK_AND_SET_JUMP_OFFSET_AT(cx, cg, pn3->pn_offset); pn4 = pn3->pn_right; ok = js_EmitTree(cx, cg, pn4); if (!ok) goto out; pn3->pn_offset = pn4->pn_offset; if (pn3->pn_type == TOK_DEFAULT) off = pn3->pn_offset - top; } if (!hasDefault) { /* If no default case, offset for default is to end of switch. */ off = CG_OFFSET(cg) - top; } /* We better have set "off" by now. */ JS_ASSERT(off != -1); /* Set the default offset (to end of switch if no default). */ if (switchOp == JSOP_CONDSWITCH) { pc = NULL; JS_ASSERT(defaultOffset != -1); ok = js_SetJumpOffset(cx, cg, CG_CODE(cg, defaultOffset), off - (defaultOffset - top)); if (!ok) goto out; } else { pc = CG_CODE(cg, top); ok = js_SetJumpOffset(cx, cg, pc, off); if (!ok) goto out; pc += JUMP_OFFSET_LEN; } /* Set the SRC_SWITCH note's offset operand to tell end of switch. */ off = CG_OFFSET(cg) - top; ok = js_SetSrcNoteOffset(cx, cg, (uintN)noteIndex, 0, off); if (!ok) goto out; if (switchOp == JSOP_TABLESWITCH) { /* Skip over the already-initialized switch bounds. */ pc += 2 * JUMP_OFFSET_LEN; /* Fill in the jump table, if there is one. */ for (i = 0; i < (jsint)tableLength; i++) { pn3 = table[i]; off = pn3 ? pn3->pn_offset - top : 0; ok = js_SetJumpOffset(cx, cg, pc, off); if (!ok) goto out; pc += JUMP_OFFSET_LEN; } } else if (switchOp == JSOP_LOOKUPSWITCH) { /* Skip over the already-initialized number of cases. */ pc += ATOM_INDEX_LEN; for (pn3 = pn2->pn_head; pn3; pn3 = pn3->pn_next) { if (pn3->pn_type == TOK_DEFAULT) continue; atom = js_AtomizeValue(cx, pn3->pn_val, 0); if (!atom) goto bad; ale = js_IndexAtom(cx, atom, &cg->atomList); if (!ale) goto bad; SET_ATOM_INDEX(pc, ALE_INDEX(ale)); pc += ATOM_INDEX_LEN; off = pn3->pn_offset - top; ok = js_SetJumpOffset(cx, cg, pc, off); if (!ok) goto out; pc += JUMP_OFFSET_LEN; } } out: if (table) JS_free(cx, table); if (ok) { ok = js_PopStatementCG(cx, cg); #if JS_HAS_BLOCK_SCOPE if (ok && pn->pn_right->pn_type == TOK_LEXICALSCOPE) { EMIT_UINT16_IMM_OP(JSOP_LEAVEBLOCK, count); cg->stackDepth -= count; } #endif } return ok; bad: ok = JS_FALSE; goto out; } JSBool js_EmitFunctionBytecode(JSContext *cx, JSCodeGenerator *cg, JSParseNode *body) { if (!js_AllocTryNotes(cx, cg)) return JS_FALSE; if (cg->treeContext.flags & TCF_FUN_IS_GENERATOR) { /* JSOP_GENERATOR must be the first instruction. */ CG_SWITCH_TO_PROLOG(cg); JS_ASSERT(CG_NEXT(cg) == CG_BASE(cg)); if (js_Emit1(cx, cg, JSOP_GENERATOR) < 0) return JS_FALSE; CG_SWITCH_TO_MAIN(cg); } return js_EmitTree(cx, cg, body) && js_Emit1(cx, cg, JSOP_STOP) >= 0; } JSBool js_EmitFunctionBody(JSContext *cx, JSCodeGenerator *cg, JSParseNode *body, JSFunction *fun) { JSStackFrame *fp, frame; JSObject *funobj; JSBool ok; fp = cx->fp; funobj = fun->object; JS_ASSERT(!fp || (fp->fun != fun && fp->varobj != funobj && fp->scopeChain != funobj)); memset(&frame, 0, sizeof frame); frame.fun = fun; frame.varobj = frame.scopeChain = funobj; frame.down = fp; frame.flags = JS_HAS_COMPILE_N_GO_OPTION(cx) ? JSFRAME_COMPILING | JSFRAME_COMPILE_N_GO : JSFRAME_COMPILING; cx->fp = &frame; ok = js_EmitFunctionBytecode(cx, cg, body); cx->fp = fp; if (!ok) return JS_FALSE; if (!js_NewScriptFromCG(cx, cg, fun)) return JS_FALSE; JS_ASSERT(FUN_INTERPRETED(fun)); return JS_TRUE; } /* A macro for inlining at the top of js_EmitTree (whence it came). */ #define UPDATE_LINE_NUMBER_NOTES(cx, cg, pn) \ JS_BEGIN_MACRO \ uintN line_ = (pn)->pn_pos.begin.lineno; \ uintN delta_ = line_ - CG_CURRENT_LINE(cg); \ if (delta_ != 0) { \ /* \ * Encode any change in the current source line number by using \ * either several SRC_NEWLINE notes or just one SRC_SETLINE note, \ * whichever consumes less space. \ * \ * NB: We handle backward line number deltas (possible with for \ * loops where the update part is emitted after the body, but its \ * line number is <= any line number in the body) here by letting \ * unsigned delta_ wrap to a very large number, which triggers a \ * SRC_SETLINE. \ */ \ CG_CURRENT_LINE(cg) = line_; \ if (delta_ >= (uintN)(2 + ((line_ > SN_3BYTE_OFFSET_MASK)<<1))) { \ if (js_NewSrcNote2(cx, cg, SRC_SETLINE, (ptrdiff_t)line_) < 0)\ return JS_FALSE; \ } else { \ do { \ if (js_NewSrcNote(cx, cg, SRC_NEWLINE) < 0) \ return JS_FALSE; \ } while (--delta_ != 0); \ } \ } \ JS_END_MACRO /* A function, so that we avoid macro-bloating all the other callsites. */ static JSBool UpdateLineNumberNotes(JSContext *cx, JSCodeGenerator *cg, JSParseNode *pn) { UPDATE_LINE_NUMBER_NOTES(cx, cg, pn); return JS_TRUE; } static JSBool MaybeEmitVarDecl(JSContext *cx, JSCodeGenerator *cg, JSOp prologOp, JSParseNode *pn, jsatomid *result) { jsatomid atomIndex; JSAtomListElement *ale; if (pn->pn_slot >= 0) { atomIndex = (jsatomid) pn->pn_slot; } else { ale = js_IndexAtom(cx, pn->pn_atom, &cg->atomList); if (!ale) return JS_FALSE; atomIndex = ALE_INDEX(ale); } if ((js_CodeSpec[pn->pn_op].format & JOF_TYPEMASK) == JOF_CONST && (!(cg->treeContext.flags & TCF_IN_FUNCTION) || (cg->treeContext.flags & TCF_FUN_HEAVYWEIGHT))) { /* Emit a prolog bytecode to predefine the variable. */ CG_SWITCH_TO_PROLOG(cg); if (!UpdateLineNumberNotes(cx, cg, pn)) return JS_FALSE; EMIT_ATOM_INDEX_OP(prologOp, atomIndex); CG_SWITCH_TO_MAIN(cg); } if (result) *result = atomIndex; return JS_TRUE; } #if JS_HAS_DESTRUCTURING typedef JSBool (*DestructuringDeclEmitter)(JSContext *cx, JSCodeGenerator *cg, JSOp prologOp, JSParseNode *pn); static JSBool EmitDestructuringDecl(JSContext *cx, JSCodeGenerator *cg, JSOp prologOp, JSParseNode *pn) { JS_ASSERT(pn->pn_type == TOK_NAME); if (!BindNameToSlot(cx, &cg->treeContext, pn, prologOp == JSOP_NOP)) return JS_FALSE; JS_ASSERT(pn->pn_op != JSOP_ARGUMENTS); return MaybeEmitVarDecl(cx, cg, prologOp, pn, NULL); } static JSBool EmitDestructuringDecls(JSContext *cx, JSCodeGenerator *cg, JSOp prologOp, JSParseNode *pn) { JSParseNode *pn2, *pn3; DestructuringDeclEmitter emitter; if (pn->pn_type == TOK_RB) { for (pn2 = pn->pn_head; pn2; pn2 = pn2->pn_next) { if (pn2->pn_type == TOK_COMMA) continue; emitter = (pn2->pn_type == TOK_NAME) ? EmitDestructuringDecl : EmitDestructuringDecls; if (!emitter(cx, cg, prologOp, pn2)) return JS_FALSE; } } else { JS_ASSERT(pn->pn_type == TOK_RC); for (pn2 = pn->pn_head; pn2; pn2 = pn2->pn_next) { pn3 = pn2->pn_right; emitter = (pn3->pn_type == TOK_NAME) ? EmitDestructuringDecl : EmitDestructuringDecls; if (!emitter(cx, cg, prologOp, pn3)) return JS_FALSE; } } return JS_TRUE; } static JSBool EmitDestructuringOpsHelper(JSContext *cx, JSCodeGenerator *cg, JSParseNode *pn); static JSBool EmitDestructuringLHS(JSContext *cx, JSCodeGenerator *cg, JSParseNode *pn, JSBool wantpop) { jsuint slot; /* Skip any parenthesization. */ while (pn->pn_type == TOK_RP) pn = pn->pn_kid; /* * Now emit the lvalue opcode sequence. If the lvalue is a nested * destructuring initialiser-form, call ourselves to handle it, then * pop the matched value. Otherwise emit an lvalue bytecode sequence * ending with a JSOP_ENUMELEM or equivalent op. */ if (pn->pn_type == TOK_RB || pn->pn_type == TOK_RC) { if (!EmitDestructuringOpsHelper(cx, cg, pn)) return JS_FALSE; if (wantpop && js_Emit1(cx, cg, JSOP_POP) < 0) return JS_FALSE; } else { if (pn->pn_type == TOK_NAME && !BindNameToSlot(cx, &cg->treeContext, pn, JS_FALSE)) { return JS_FALSE; } switch (pn->pn_op) { case JSOP_SETNAME: /* * NB: pn is a PN_NAME node, not a PN_BINARY. Nevertheless, * we want to emit JSOP_ENUMELEM, which has format JOF_ELEM. * So here and for JSOP_ENUMCONSTELEM, we use EmitElemOp. */ if (!EmitElemOp(cx, pn, JSOP_ENUMELEM, cg)) return JS_FALSE; break; case JSOP_SETCONST: if (!EmitElemOp(cx, pn, JSOP_ENUMCONSTELEM, cg)) return JS_FALSE; break; case JSOP_SETLOCAL: if (wantpop) { slot = (jsuint) pn->pn_slot; EMIT_UINT16_IMM_OP(JSOP_SETLOCALPOP, slot); break; } /* FALL THROUGH */ case JSOP_SETARG: case JSOP_SETVAR: case JSOP_SETGVAR: slot = (jsuint) pn->pn_slot; EMIT_UINT16_IMM_OP(pn->pn_op, slot); if (wantpop && js_Emit1(cx, cg, JSOP_POP) < 0) return JS_FALSE; break; default: #if JS_HAS_LVALUE_RETURN || JS_HAS_XML_SUPPORT { ptrdiff_t top; top = CG_OFFSET(cg); if (!js_EmitTree(cx, cg, pn)) return JS_FALSE; if (js_NewSrcNote2(cx, cg, SRC_PCBASE, CG_OFFSET(cg) - top) < 0) return JS_FALSE; if (js_Emit1(cx, cg, JSOP_ENUMELEM) < 0) return JS_FALSE; break; } #endif case JSOP_ENUMELEM: JS_ASSERT(0); } } return JS_TRUE; } /* * Recursive helper for EmitDestructuringOps. * * Given a value to destructure on the stack, walk over an object or array * initialiser at pn, emitting bytecodes to match property values and store * them in the lvalues identified by the matched property names. */ static JSBool EmitDestructuringOpsHelper(JSContext *cx, JSCodeGenerator *cg, JSParseNode *pn) { jsuint index; JSParseNode *pn2, *pn3; JSBool doElemOp; #ifdef DEBUG intN stackDepth = cg->stackDepth; JS_ASSERT(stackDepth != 0); JS_ASSERT(pn->pn_arity == PN_LIST); JS_ASSERT(pn->pn_type == TOK_RB || pn->pn_type == TOK_RC); #endif if (pn->pn_count == 0) { /* Emit a DUP;POP sequence for the decompiler. */ return js_Emit1(cx, cg, JSOP_DUP) >= 0 && js_Emit1(cx, cg, JSOP_POP) >= 0; } index = 0; for (pn2 = pn->pn_head; pn2; pn2 = pn2->pn_next) { /* * Duplicate the value being destructured to use as a reference base. */ if (js_Emit1(cx, cg, JSOP_DUP) < 0) return JS_FALSE; /* * Now push the property name currently being matched, which is either * the array initialiser's current index, or the current property name * "label" on the left of a colon in the object initialiser. Set pn3 * to the lvalue node, which is in the value-initializing position. */ doElemOp = JS_TRUE; if (pn->pn_type == TOK_RB) { if (!EmitNumberOp(cx, index, cg)) return JS_FALSE; pn3 = pn2; } else { JS_ASSERT(pn->pn_type == TOK_RC); JS_ASSERT(pn2->pn_type == TOK_COLON); pn3 = pn2->pn_left; if (pn3->pn_type == TOK_NUMBER) { /* * If we are emitting an object destructuring initialiser, * annotate the index op with SRC_INITPROP so we know we are * not decompiling an array initialiser. */ if (js_NewSrcNote(cx, cg, SRC_INITPROP) < 0) return JS_FALSE; if (!EmitNumberOp(cx, pn3->pn_dval, cg)) return JS_FALSE; } else { JS_ASSERT(pn3->pn_type == TOK_STRING || pn3->pn_type == TOK_NAME); if (!EmitAtomOp(cx, pn3, JSOP_GETPROP, cg)) return JS_FALSE; doElemOp = JS_FALSE; } pn3 = pn2->pn_right; } if (doElemOp) { /* * Ok, get the value of the matching property name. This leaves * that value on top of the value being destructured, so the stack * is one deeper than when we started. */ if (js_Emit1(cx, cg, JSOP_GETELEM) < 0) return JS_FALSE; JS_ASSERT(cg->stackDepth == stackDepth + 1); } /* Nullary comma node makes a hole in the array destructurer. */ if (pn3->pn_type == TOK_COMMA && pn3->pn_arity == PN_NULLARY) { JS_ASSERT(pn->pn_type == TOK_RB); JS_ASSERT(pn2 == pn3); if (js_Emit1(cx, cg, JSOP_POP) < 0) return JS_FALSE; } else { if (!EmitDestructuringLHS(cx, cg, pn3, JS_TRUE)) return JS_FALSE; } JS_ASSERT(cg->stackDepth == stackDepth); ++index; } return JS_TRUE; } static ptrdiff_t OpToDeclType(JSOp op) { switch (op) { case JSOP_NOP: return SRC_DECL_LET; case JSOP_DEFCONST: return SRC_DECL_CONST; case JSOP_DEFVAR: return SRC_DECL_VAR; default: return SRC_DECL_NONE; } } static JSBool EmitDestructuringOps(JSContext *cx, JSCodeGenerator *cg, JSOp declOp, JSParseNode *pn) { /* * If we're called from a variable declaration, help the decompiler by * annotating the first JSOP_DUP that EmitDestructuringOpsHelper emits. * If the destructuring initialiser is empty, our helper will emit a * JSOP_DUP followed by a JSOP_POP for the decompiler. */ if (js_NewSrcNote2(cx, cg, SRC_DESTRUCT, OpToDeclType(declOp)) < 0) return JS_FALSE; /* * Call our recursive helper to emit the destructuring assignments and * related stack manipulations. */ return EmitDestructuringOpsHelper(cx, cg, pn); } static JSBool EmitGroupAssignment(JSContext *cx, JSCodeGenerator *cg, JSOp declOp, JSParseNode *lhs, JSParseNode *rhs) { jsuint depth, limit, slot; JSParseNode *pn; depth = limit = (uintN) cg->stackDepth; for (pn = rhs->pn_head; pn; pn = pn->pn_next) { if (limit == JS_BIT(16)) { js_ReportCompileErrorNumber(cx, rhs, JSREPORT_PN | JSREPORT_ERROR, JSMSG_ARRAY_INIT_TOO_BIG); return JS_FALSE; } if (pn->pn_type == TOK_COMMA) { if (js_Emit1(cx, cg, JSOP_PUSH) < 0) return JS_FALSE; } else { JS_ASSERT(pn->pn_type != TOK_DEFSHARP); if (!js_EmitTree(cx, cg, pn)) return JS_FALSE; } ++limit; } if (js_NewSrcNote2(cx, cg, SRC_GROUPASSIGN, OpToDeclType(declOp)) < 0) return JS_FALSE; slot = depth; for (pn = lhs->pn_head; pn; pn = pn->pn_next) { if (slot < limit) { EMIT_UINT16_IMM_OP(JSOP_GETLOCAL, slot); } else { if (js_Emit1(cx, cg, JSOP_PUSH) < 0) return JS_FALSE; } if (pn->pn_type == TOK_COMMA && pn->pn_arity == PN_NULLARY) { if (js_Emit1(cx, cg, JSOP_POP) < 0) return JS_FALSE; } else { if (!EmitDestructuringLHS(cx, cg, pn, pn->pn_next != NULL)) return JS_FALSE; } ++slot; } EMIT_UINT16_IMM_OP(JSOP_SETSP, (jsatomid)depth); cg->stackDepth = (uintN) depth; return JS_TRUE; } /* * Helper called with pop out param initialized to a JSOP_POP* opcode. If we * can emit a group assignment sequence, which results in 0 stack depth delta, * we set *pop to JSOP_NOP so callers can veto emitting pn followed by a pop. */ static JSBool MaybeEmitGroupAssignment(JSContext *cx, JSCodeGenerator *cg, JSOp declOp, JSParseNode *pn, JSOp *pop) { JSParseNode *lhs, *rhs; JS_ASSERT(pn->pn_type == TOK_ASSIGN); JS_ASSERT(*pop == JSOP_POP || *pop == JSOP_POPV); lhs = pn->pn_left; rhs = pn->pn_right; if (lhs->pn_type == TOK_RB && rhs->pn_type == TOK_RB && lhs->pn_count <= rhs->pn_count && (rhs->pn_count == 0 || rhs->pn_head->pn_type != TOK_DEFSHARP)) { if (!EmitGroupAssignment(cx, cg, declOp, lhs, rhs)) return JS_FALSE; *pop = JSOP_NOP; } return JS_TRUE; } #endif /* JS_HAS_DESTRUCTURING */ static JSBool EmitVariables(JSContext *cx, JSCodeGenerator *cg, JSParseNode *pn, JSBool inLetHead, ptrdiff_t *headNoteIndex) { JSTreeContext *tc; JSBool let, forInVar; #if JS_HAS_BLOCK_SCOPE JSBool forInLet, popScope; JSStmtInfo *stmt, *scopeStmt; #endif ptrdiff_t off, noteIndex, tmp; JSParseNode *pn2, *pn3; JSOp op; jsatomid atomIndex; uintN oldflags; /* Default in case of JS_HAS_BLOCK_SCOPE early return, below. */ *headNoteIndex = -1; /* * Let blocks and expressions have a parenthesized head in which the new * scope is not yet open. Initializer evaluation uses the parent node's * lexical scope. If popScope is true below, then we hide the top lexical * block from any calls to BindNameToSlot hiding in pn2->pn_expr so that * it won't find any names in the new let block. * * The same goes for let declarations in the head of any kind of for loop. * Unlike a let declaration 'let x = i' within a block, where x is hoisted * to the start of the block, a 'for (let x = i...) ...' loop evaluates i * in the containing scope, and puts x in the loop body's scope. */ tc = &cg->treeContext; let = (pn->pn_op == JSOP_NOP); forInVar = (pn->pn_extra & PNX_FORINVAR) != 0; #if JS_HAS_BLOCK_SCOPE forInLet = let && forInVar; popScope = (inLetHead || (let && (tc->flags & TCF_IN_FOR_INIT))); JS_ASSERT(!popScope || let); #endif off = noteIndex = -1; for (pn2 = pn->pn_head; ; pn2 = pn2->pn_next) { #if JS_HAS_DESTRUCTURING if (pn2->pn_type != TOK_NAME) { if (pn2->pn_type == TOK_RB || pn2->pn_type == TOK_RC) { /* * Emit variable binding ops, but not destructuring ops. * The parser (see Variables, jsparse.c) has ensured that * our caller will be the TOK_FOR/TOK_IN case in js_EmitTree, * and that case will emit the destructuring code only after * emitting an enumerating opcode and a branch that tests * whether the enumeration ended. */ JS_ASSERT(forInVar); JS_ASSERT(pn->pn_count == 1); if (!EmitDestructuringDecls(cx, cg, pn->pn_op, pn2)) return JS_FALSE; break; } /* * A destructuring initialiser assignment preceded by var is * always evaluated promptly, even if it is to the left of 'in' * in a for-in loop. As with 'for (var x = i in o)...', this * will cause the entire 'var [a, b] = i' to be hoisted out of * the head of the loop. */ JS_ASSERT(pn2->pn_type == TOK_ASSIGN); if (pn->pn_count == 1 && !forInLet) { /* * If this is the only destructuring assignment in the list, * try to optimize to a group assignment. If we're in a let * head, pass JSOP_POP rather than the pseudo-prolog JSOP_NOP * in pn->pn_op, to suppress a second (and misplaced) 'let'. */ JS_ASSERT(noteIndex < 0 && !pn2->pn_next); op = JSOP_POP; if (!MaybeEmitGroupAssignment(cx, cg, inLetHead ? JSOP_POP : pn->pn_op, pn2, &op)) { return JS_FALSE; } if (op == JSOP_NOP) { pn->pn_extra = (pn->pn_extra & ~PNX_POPVAR) | PNX_GROUPINIT; break; } } pn3 = pn2->pn_left; if (!EmitDestructuringDecls(cx, cg, pn->pn_op, pn3)) return JS_FALSE; #if JS_HAS_BLOCK_SCOPE /* * If this is a 'for (let [x, y] = i in o) ...' let declaration, * throw away i if it is a useless expression. */ if (forInLet) { JSBool useful = JS_FALSE; JS_ASSERT(pn->pn_count == 1); if (!CheckSideEffects(cx, tc, pn2->pn_right, &useful)) return JS_FALSE; if (!useful) return JS_TRUE; } #endif if (!js_EmitTree(cx, cg, pn2->pn_right)) return JS_FALSE; #if JS_HAS_BLOCK_SCOPE /* * The expression i in 'for (let [x, y] = i in o) ...', which is * pn2->pn_right above, appears to have side effects. We've just * emitted code to evaluate i, but we must not destructure i yet. * Let the TOK_FOR: code in js_EmitTree do the destructuring to * emit the right combination of source notes and bytecode for the * decompiler. * * This has the effect of hoisting the evaluation of i out of the * for-in loop, without hoisting the let variables, which must of * course be scoped by the loop. Set PNX_POPVAR to cause JSOP_POP * to be emitted, just before returning from this function. */ if (forInVar) { pn->pn_extra |= PNX_POPVAR; if (forInLet) break; } #endif /* * Veto pn->pn_op if inLetHead to avoid emitting a SRC_DESTRUCT * that's redundant with respect to the SRC_DECL/SRC_DECL_LET that * we will emit at the bottom of this function. */ if (!EmitDestructuringOps(cx, cg, inLetHead ? JSOP_POP : pn->pn_op, pn3)) { return JS_FALSE; } goto emit_note_pop; } #else JS_ASSERT(pn2->pn_type == TOK_NAME); #endif if (!BindNameToSlot(cx, &cg->treeContext, pn2, let)) return JS_FALSE; JS_ASSERT(pn2->pn_slot >= 0 || !let); op = pn2->pn_op; if (op == JSOP_ARGUMENTS) { /* JSOP_ARGUMENTS => no initializer */ JS_ASSERT(!pn2->pn_expr && !let); pn3 = NULL; #ifdef __GNUC__ atomIndex = 0; /* quell GCC overwarning */ #endif } else { if (!MaybeEmitVarDecl(cx, cg, pn->pn_op, pn2, &atomIndex)) return JS_FALSE; pn3 = pn2->pn_expr; if (pn3) { #if JS_HAS_BLOCK_SCOPE /* * If this is a 'for (let x = i in o) ...' let declaration, * throw away i if it is a useless expression. */ if (forInLet) { JSBool useful = JS_FALSE; JS_ASSERT(pn->pn_count == 1); if (!CheckSideEffects(cx, tc, pn3, &useful)) return JS_FALSE; if (!useful) return JS_TRUE; } #endif if (op == JSOP_SETNAME) { JS_ASSERT(!let); EMIT_ATOM_INDEX_OP(JSOP_BINDNAME, atomIndex); } if (pn->pn_op == JSOP_DEFCONST && !js_DefineCompileTimeConstant(cx, cg, pn2->pn_atom, pn3)) { return JS_FALSE; } #if JS_HAS_BLOCK_SCOPE /* Evaluate expr in the outer lexical scope if requested. */ if (popScope) { stmt = tc->topStmt; scopeStmt = tc->topScopeStmt; tc->topStmt = stmt->down; tc->topScopeStmt = scopeStmt->downScope; } #ifdef __GNUC__ else { stmt = scopeStmt = NULL; /* quell GCC overwarning */ } #endif #endif oldflags = cg->treeContext.flags; cg->treeContext.flags &= ~TCF_IN_FOR_INIT; if (!js_EmitTree(cx, cg, pn3)) return JS_FALSE; cg->treeContext.flags |= oldflags & TCF_IN_FOR_INIT; #if JS_HAS_BLOCK_SCOPE if (popScope) { tc->topStmt = stmt; tc->topScopeStmt = scopeStmt; } #endif } } /* * 'for (var x in o) ...' and 'for (var x = i in o) ...' call the * TOK_VAR case, but only the initialized case (a strange one that * falls out of ECMA-262's grammar) wants to run past this point. * Both cases must conditionally emit a JSOP_DEFVAR, above. Note * that the parser error-checks to ensure that pn->pn_count is 1. * * 'for (let x = i in o) ...' must evaluate i before the loop, and * subject it to useless expression elimination. The variable list * in pn is a single let declaration if pn_op == JSOP_NOP. We test * the let local in order to break early in this case, as well as in * the 'for (var x in o)' case. * * XXX Narcissus keeps track of variable declarations in the node * for the script being compiled, so there's no need to share any * conditional prolog code generation there. We could do likewise, * but it's a big change, requiring extra allocation, so probably * not worth the trouble for SpiderMonkey. */ JS_ASSERT(pn3 == pn2->pn_expr); if (forInVar && (!pn3 || let)) { JS_ASSERT(pn->pn_count == 1); break; } if (pn2 == pn->pn_head && !inLetHead && js_NewSrcNote2(cx, cg, SRC_DECL, (pn->pn_op == JSOP_DEFCONST) ? SRC_DECL_CONST : (pn->pn_op == JSOP_DEFVAR) ? SRC_DECL_VAR : SRC_DECL_LET) < 0) { return JS_FALSE; } if (op == JSOP_ARGUMENTS) { if (js_Emit1(cx, cg, op) < 0) return JS_FALSE; } else if (pn2->pn_slot >= 0) { EMIT_UINT16_IMM_OP(op, atomIndex); } else { EMIT_ATOM_INDEX_OP(op, atomIndex); } #if JS_HAS_DESTRUCTURING emit_note_pop: #endif tmp = CG_OFFSET(cg); if (noteIndex >= 0) { if (!js_SetSrcNoteOffset(cx, cg, (uintN)noteIndex, 0, tmp-off)) return JS_FALSE; } if (!pn2->pn_next) break; off = tmp; noteIndex = js_NewSrcNote2(cx, cg, SRC_PCDELTA, 0); if (noteIndex < 0 || js_Emit1(cx, cg, JSOP_POP) < 0) return JS_FALSE; } /* If this is a let head, emit and return a srcnote on the pop. */ if (inLetHead) { *headNoteIndex = js_NewSrcNote(cx, cg, SRC_DECL); if (*headNoteIndex < 0) return JS_FALSE; if (!(pn->pn_extra & PNX_POPVAR)) return js_Emit1(cx, cg, JSOP_NOP) >= 0; } return !(pn->pn_extra & PNX_POPVAR) || js_Emit1(cx, cg, JSOP_POP) >= 0; } #if defined DEBUG_brendan || defined DEBUG_mrbkap static JSBool GettableNoteForNextOp(JSCodeGenerator *cg) { ptrdiff_t offset, target; jssrcnote *sn, *end; offset = 0; target = CG_OFFSET(cg); for (sn = CG_NOTES(cg), end = sn + CG_NOTE_COUNT(cg); sn < end; sn = SN_NEXT(sn)) { if (offset == target && SN_IS_GETTABLE(sn)) return JS_TRUE; offset += SN_DELTA(sn); } return JS_FALSE; } #endif JSBool js_EmitTree(JSContext *cx, JSCodeGenerator *cg, JSParseNode *pn) { JSBool ok, useful, wantval; JSStmtInfo *stmt, stmtInfo; ptrdiff_t top, off, tmp, beq, jmp; JSParseNode *pn2, *pn3; JSAtom *atom; JSAtomListElement *ale; jsatomid atomIndex; ptrdiff_t noteIndex; JSSrcNoteType noteType; jsbytecode *pc; JSOp op; JSTokenType type; uint32 argc; int stackDummy; if (!JS_CHECK_STACK_SIZE(cx, stackDummy)) { JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_OVER_RECURSED); return JS_FALSE; } ok = JS_TRUE; cg->emitLevel++; pn->pn_offset = top = CG_OFFSET(cg); /* Emit notes to tell the current bytecode's source line number. */ UPDATE_LINE_NUMBER_NOTES(cx, cg, pn); switch (pn->pn_type) { case TOK_FUNCTION: { void *cg2mark; JSCodeGenerator *cg2; JSFunction *fun; #if JS_HAS_XML_SUPPORT if (pn->pn_arity == PN_NULLARY) { if (js_Emit1(cx, cg, JSOP_GETFUNNS) < 0) return JS_FALSE; break; } #endif /* Generate code for the function's body. */ cg2mark = JS_ARENA_MARK(&cx->tempPool); JS_ARENA_ALLOCATE_TYPE(cg2, JSCodeGenerator, &cx->tempPool); if (!cg2) { JS_ReportOutOfMemory(cx); return JS_FALSE; } if (!js_InitCodeGenerator(cx, cg2, cg->codePool, cg->notePool, cg->filename, pn->pn_pos.begin.lineno, cg->principals)) { return JS_FALSE; } cg2->treeContext.flags = (uint16) (pn->pn_flags | TCF_IN_FUNCTION); cg2->treeContext.tryCount = pn->pn_tryCount; cg2->parent = cg; fun = (JSFunction *) JS_GetPrivate(cx, ATOM_TO_OBJECT(pn->pn_funAtom)); if (!js_EmitFunctionBody(cx, cg2, pn->pn_body, fun)) return JS_FALSE; /* * We need an activation object if an inner peeks out, or if such * inner-peeking caused one of our inners to become heavyweight. */ if (cg2->treeContext.flags & (TCF_FUN_USES_NONLOCALS | TCF_FUN_HEAVYWEIGHT)) { cg->treeContext.flags |= TCF_FUN_HEAVYWEIGHT; } js_FinishCodeGenerator(cx, cg2); JS_ARENA_RELEASE(&cx->tempPool, cg2mark); /* Make the function object a literal in the outer script's pool. */ ale = js_IndexAtom(cx, pn->pn_funAtom, &cg->atomList); if (!ale) return JS_FALSE; atomIndex = ALE_INDEX(ale); /* Emit a bytecode pointing to the closure object in its immediate. */ if (pn->pn_op != JSOP_NOP) { EMIT_ATOM_INDEX_OP(pn->pn_op, atomIndex); break; } /* Top-level named functions need a nop for decompilation. */ noteIndex = js_NewSrcNote2(cx, cg, SRC_FUNCDEF, (ptrdiff_t)atomIndex); if (noteIndex < 0 || js_Emit1(cx, cg, JSOP_NOP) < 0) { return JS_FALSE; } /* * Top-levels also need a prolog op to predefine their names in the * variable object, or if local, to fill their stack slots. */ CG_SWITCH_TO_PROLOG(cg); if (cg->treeContext.flags & TCF_IN_FUNCTION) { JSObject *obj, *pobj; JSProperty *prop; JSScopeProperty *sprop; uintN slot; obj = OBJ_GET_PARENT(cx, fun->object); if (!js_LookupHiddenProperty(cx, obj, ATOM_TO_JSID(fun->atom), &pobj, &prop)) { return JS_FALSE; } JS_ASSERT(prop && pobj == obj); sprop = (JSScopeProperty *) prop; JS_ASSERT(sprop->getter == js_GetLocalVariable); slot = sprop->shortid; OBJ_DROP_PROPERTY(cx, pobj, prop); /* * If this local function is declared in a body block induced by * let declarations, reparent fun->object to the compiler-created * body block object so that JSOP_DEFLOCALFUN can clone that block * into the runtime scope chain. */ stmt = cg->treeContext.topStmt; if (stmt && stmt->type == STMT_BLOCK && stmt->down && stmt->down->type == STMT_BLOCK && (stmt->down->flags & SIF_SCOPE)) { obj = ATOM_TO_OBJECT(stmt->down->atom); JS_ASSERT(LOCKED_OBJ_GET_CLASS(obj) == &js_BlockClass); OBJ_SET_PARENT(cx, fun->object, obj); } if (atomIndex >= JS_BIT(16)) { /* * Lots of literals in the outer function, so we have to emit * [JSOP_LITOPX, atomIndex, JSOP_DEFLOCALFUN, var slot]. */ off = js_EmitN(cx, cg, JSOP_LITOPX, 3); if (off < 0) return JS_FALSE; pc = CG_CODE(cg, off); SET_LITERAL_INDEX(pc, atomIndex); EMIT_UINT16_IMM_OP(JSOP_DEFLOCALFUN, slot); } else { /* Emit [JSOP_DEFLOCALFUN, var slot, atomIndex]. */ off = js_EmitN(cx, cg, JSOP_DEFLOCALFUN, VARNO_LEN + ATOM_INDEX_LEN); if (off < 0) return JS_FALSE; pc = CG_CODE(cg, off); SET_VARNO(pc, slot); pc += VARNO_LEN; SET_ATOM_INDEX(pc, atomIndex); } } else { JS_ASSERT(!cg->treeContext.topStmt); EMIT_ATOM_INDEX_OP(JSOP_DEFFUN, atomIndex); } CG_SWITCH_TO_MAIN(cg); break; } #if JS_HAS_EXPORT_IMPORT case TOK_EXPORT: pn2 = pn->pn_head; if (pn2->pn_type == TOK_STAR) { /* * 'export *' must have no other elements in the list (what would * be the point?). */ if (js_Emit1(cx, cg, JSOP_EXPORTALL) < 0) return JS_FALSE; } else { /* * If not 'export *', the list consists of NAME nodes identifying * properties of the variables object to flag as exported. */ do { ale = js_IndexAtom(cx, pn2->pn_atom, &cg->atomList); if (!ale) return JS_FALSE; EMIT_ATOM_INDEX_OP(JSOP_EXPORTNAME, ALE_INDEX(ale)); } while ((pn2 = pn2->pn_next) != NULL); } break; case TOK_IMPORT: for (pn2 = pn->pn_head; pn2; pn2 = pn2->pn_next) { /* * Each subtree on an import list is rooted by a DOT or LB node. * A DOT may have a null pn_atom member, in which case pn_op must * be JSOP_IMPORTALL -- see EmitPropOp above. */ if (!js_EmitTree(cx, cg, pn2)) return JS_FALSE; } break; #endif /* JS_HAS_EXPORT_IMPORT */ case TOK_IF: /* Initialize so we can detect else-if chains and avoid recursion. */ stmtInfo.type = STMT_IF; beq = jmp = -1; noteIndex = -1; if_again: /* Emit code for the condition before pushing stmtInfo. */ if (!js_EmitTree(cx, cg, pn->pn_kid1)) return JS_FALSE; top = CG_OFFSET(cg); if (stmtInfo.type == STMT_IF) { js_PushStatement(&cg->treeContext, &stmtInfo, STMT_IF, top); } else { /* * We came here from the goto further below that detects else-if * chains, so we must mutate stmtInfo back into a STMT_IF record. * Also (see below for why) we need a note offset for SRC_IF_ELSE * to help the decompiler. Actually, we need two offsets, one for * decompiling any else clause and the second for decompiling an * else-if chain without bracing, overindenting, or incorrectly * scoping let declarations. */ JS_ASSERT(stmtInfo.type == STMT_ELSE); stmtInfo.type = STMT_IF; stmtInfo.update = top; if (!js_SetSrcNoteOffset(cx, cg, noteIndex, 0, jmp - beq)) return JS_FALSE; if (!js_SetSrcNoteOffset(cx, cg, noteIndex, 1, top - jmp)) return JS_FALSE; } /* Emit an annotated branch-if-false around the then part. */ pn3 = pn->pn_kid3; noteIndex = js_NewSrcNote(cx, cg, pn3 ? SRC_IF_ELSE : SRC_IF); if (noteIndex < 0) return JS_FALSE; beq = EmitJump(cx, cg, JSOP_IFEQ, 0); if (beq < 0) return JS_FALSE; /* Emit code for the then and optional else parts. */ if (!js_EmitTree(cx, cg, pn->pn_kid2)) return JS_FALSE; if (pn3) { /* Modify stmtInfo so we know we're in the else part. */ stmtInfo.type = STMT_ELSE; /* * Emit a JSOP_BACKPATCH op to jump from the end of our then part * around the else part. The js_PopStatementCG call at the bottom * of this switch case will fix up the backpatch chain linked from * stmtInfo.breaks. */ jmp = EmitGoto(cx, cg, &stmtInfo, &stmtInfo.breaks, NULL, SRC_NULL); if (jmp < 0) return JS_FALSE; /* Ensure the branch-if-false comes here, then emit the else. */ CHECK_AND_SET_JUMP_OFFSET_AT(cx, cg, beq); if (pn3->pn_type == TOK_IF) { pn = pn3; goto if_again; } if (!js_EmitTree(cx, cg, pn3)) return JS_FALSE; /* * Annotate SRC_IF_ELSE with the offset from branch to jump, for * the decompiler's benefit. We can't just "back up" from the pc * of the else clause, because we don't know whether an extended * jump was required to leap from the end of the then clause over * the else clause. */ if (!js_SetSrcNoteOffset(cx, cg, noteIndex, 0, jmp - beq)) return JS_FALSE; } else { /* No else part, fixup the branch-if-false to come here. */ CHECK_AND_SET_JUMP_OFFSET_AT(cx, cg, beq); } ok = js_PopStatementCG(cx, cg); break; case TOK_SWITCH: /* Out of line to avoid bloating js_EmitTree's stack frame size. */ ok = EmitSwitch(cx, cg, pn, &stmtInfo); break; case TOK_WHILE: js_PushStatement(&cg->treeContext, &stmtInfo, STMT_WHILE_LOOP, top); if (!js_EmitTree(cx, cg, pn->pn_left)) return JS_FALSE; noteIndex = js_NewSrcNote(cx, cg, SRC_WHILE); if (noteIndex < 0) return JS_FALSE; beq = EmitJump(cx, cg, JSOP_IFEQ, 0); if (beq < 0) return JS_FALSE; if (!js_EmitTree(cx, cg, pn->pn_right)) return JS_FALSE; jmp = EmitJump(cx, cg, JSOP_GOTO, top - CG_OFFSET(cg)); if (jmp < 0) return JS_FALSE; CHECK_AND_SET_JUMP_OFFSET_AT(cx, cg, beq); if (!js_SetSrcNoteOffset(cx, cg, noteIndex, 0, jmp - beq)) return JS_FALSE; ok = js_PopStatementCG(cx, cg); break; case TOK_DO: /* Emit an annotated nop so we know to decompile a 'do' keyword. */ if (js_NewSrcNote(cx, cg, SRC_WHILE) < 0 || js_Emit1(cx, cg, JSOP_NOP) < 0) { return JS_FALSE; } /* Compile the loop body. */ top = CG_OFFSET(cg); js_PushStatement(&cg->treeContext, &stmtInfo, STMT_DO_LOOP, top); if (!js_EmitTree(cx, cg, pn->pn_left)) return JS_FALSE; /* Set loop and enclosing label update offsets, for continue. */ stmt = &stmtInfo; do { stmt->update = CG_OFFSET(cg); } while ((stmt = stmt->down) != NULL && stmt->type == STMT_LABEL); /* Compile the loop condition, now that continues know where to go. */ if (!js_EmitTree(cx, cg, pn->pn_right)) return JS_FALSE; /* * No source note needed, because JSOP_IFNE is used only for do-while. * If we ever use JSOP_IFNE for other purposes, we can still avoid yet * another note here, by storing (jmp - top) in the SRC_WHILE note's * offset, and fetching that delta in order to decompile recursively. */ if (EmitJump(cx, cg, JSOP_IFNE, top - CG_OFFSET(cg)) < 0) return JS_FALSE; ok = js_PopStatementCG(cx, cg); break; case TOK_FOR: beq = 0; /* suppress gcc warnings */ pn2 = pn->pn_left; js_PushStatement(&cg->treeContext, &stmtInfo, STMT_FOR_LOOP, top); if (pn2->pn_type == TOK_IN) { JSBool emitIFEQ; /* Set stmtInfo type for later testing. */ stmtInfo.type = STMT_FOR_IN_LOOP; noteIndex = -1; /* * If the left part is 'var x', emit code to define x if necessary * using a prolog opcode, but do not emit a pop. If the left part * is 'var x = i', emit prolog code to define x if necessary; then * emit code to evaluate i, assign the result to x, and pop the * result off the stack. * * All the logic to do this is implemented in the outer switch's * TOK_VAR case, conditioned on pn_extra flags set by the parser. * * In the 'for (var x = i in o) ...' case, the js_EmitTree(...pn3) * called here will generate the proper note for the assignment * op that sets x = i, hoisting the initialized var declaration * out of the loop: 'var x = i; for (x in o) ...'. * * In the 'for (var x in o) ...' case, nothing but the prolog op * (if needed) should be generated here, we must emit the note * just before the JSOP_FOR* opcode in the switch on pn3->pn_type * a bit below, so nothing is hoisted: 'for (var x in o) ...'. * * A 'for (let x = i in o)' loop must not be hoisted, since in * this form the let variable is scoped by the loop body (but not * the head). The initializer expression i must be evaluated for * any side effects. So we hoist only i in the let case. */ pn3 = pn2->pn_left; type = pn3->pn_type; cg->treeContext.flags |= TCF_IN_FOR_INIT; if (TOKEN_TYPE_IS_DECL(type) && !js_EmitTree(cx, cg, pn3)) return JS_FALSE; cg->treeContext.flags &= ~TCF_IN_FOR_INIT; /* Emit a push to allocate the iterator. */ if (js_Emit1(cx, cg, JSOP_STARTITER) < 0) return JS_FALSE; /* Compile the object expression to the right of 'in'. */ if (!js_EmitTree(cx, cg, pn2->pn_right)) return JS_FALSE; /* * Emit a bytecode to convert top of stack value to the iterator * object depending on the loop variant (for-in, for-each-in, or * destructuring for-in). */ #if JS_HAS_DESTRUCTURING JS_ASSERT(pn->pn_op == JSOP_FORIN || pn->pn_op == JSOP_FOREACHKEYVAL || pn->pn_op == JSOP_FOREACH); #else JS_ASSERT(pn->pn_op == JSOP_FORIN || pn->pn_op == JSOP_FOREACH); #endif if (js_Emit1(cx, cg, pn->pn_op) < 0) return JS_FALSE; top = CG_OFFSET(cg); SET_STATEMENT_TOP(&stmtInfo, top); /* * Compile a JSOP_FOR* bytecode based on the left hand side. * * Initialize op to JSOP_SETNAME in case of |for ([a, b] in o)...| * or similar, to signify assignment, rather than declaration, to * the decompiler. EmitDestructuringOps takes a prolog bytecode * parameter and emits the appropriate source note, defaulting to * assignment, so JSOP_SETNAME is not critical here; many similar * ops could be used -- just not JSOP_NOP (which means 'let'). */ emitIFEQ = JS_TRUE; op = JSOP_SETNAME; switch (type) { #if JS_HAS_BLOCK_SCOPE case TOK_LET: #endif case TOK_VAR: JS_ASSERT(pn3->pn_arity == PN_LIST && pn3->pn_count == 1); pn3 = pn3->pn_head; #if JS_HAS_DESTRUCTURING if (pn3->pn_type == TOK_ASSIGN) { pn3 = pn3->pn_left; JS_ASSERT(pn3->pn_type == TOK_RB || pn3->pn_type == TOK_RC); } if (pn3->pn_type == TOK_RB || pn3->pn_type == TOK_RC) { op = pn2->pn_left->pn_op; goto destructuring_for; } #else JS_ASSERT(pn3->pn_type == TOK_NAME); #endif /* * Always annotate JSOP_FORLOCAL if given input of the form * 'for (let x in * o)' -- the decompiler must not hoist the * 'let x' out of the loop head, or x will be bound in the * wrong scope. Likewise, but in this case only for the sake * of higher decompilation fidelity only, do not hoist 'var x' * when given 'for (var x in o)'. But 'for (var x = i in o)' * requires hoisting in order to preserve the initializer i. * The decompiler can only handle so much! */ if (( #if JS_HAS_BLOCK_SCOPE type == TOK_LET || #endif !pn3->pn_expr) && js_NewSrcNote2(cx, cg, SRC_DECL, type == TOK_VAR ? SRC_DECL_VAR : SRC_DECL_LET) < 0) { return JS_FALSE; } /* FALL THROUGH */ case TOK_NAME: if (pn3->pn_slot >= 0) { op = pn3->pn_op; switch (op) { case JSOP_GETARG: /* FALL THROUGH */ case JSOP_SETARG: op = JSOP_FORARG; break; case JSOP_GETVAR: /* FALL THROUGH */ case JSOP_SETVAR: op = JSOP_FORVAR; break; case JSOP_GETGVAR: /* FALL THROUGH */ case JSOP_SETGVAR: op = JSOP_FORNAME; break; case JSOP_GETLOCAL: /* FALL THROUGH */ case JSOP_SETLOCAL: op = JSOP_FORLOCAL; break; default: JS_ASSERT(0); } } else { pn3->pn_op = JSOP_FORNAME; if (!BindNameToSlot(cx, &cg->treeContext, pn3, JS_FALSE)) return JS_FALSE; op = pn3->pn_op; } if (pn3->pn_slot >= 0) { if (pn3->pn_attrs & JSPROP_READONLY) { JS_ASSERT(op == JSOP_FORVAR); op = JSOP_GETVAR; } atomIndex = (jsatomid) pn3->pn_slot; EMIT_UINT16_IMM_OP(op, atomIndex); } else { if (!EmitAtomOp(cx, pn3, op, cg)) return JS_FALSE; } break; case TOK_DOT: useful = JS_FALSE; if (!CheckSideEffects(cx, &cg->treeContext, pn3->pn_expr, &useful)) { return JS_FALSE; } if (!useful) { if (!EmitPropOp(cx, pn3, JSOP_FORPROP, cg)) return JS_FALSE; break; } /* FALL THROUGH */ #if JS_HAS_DESTRUCTURING case TOK_RB: case TOK_RC: destructuring_for: #endif #if JS_HAS_XML_SUPPORT case TOK_UNARYOP: #endif #if JS_HAS_LVALUE_RETURN case TOK_LP: #endif case TOK_LB: /* * We separate the first/next bytecode from the enumerator * variable binding to avoid any side-effects in the index * expression (e.g., for (x[i++] in {}) should not bind x[i] * or increment i at all). */ emitIFEQ = JS_FALSE; if (!js_Emit1(cx, cg, JSOP_FORELEM)) return JS_FALSE; /* * Emit a SRC_WHILE note with offset telling the distance to * the loop-closing jump (we can't reckon from the branch at * the top of the loop, because the loop-closing jump might * need to be an extended jump, independent of whether the * branch is short or long). */ noteIndex = js_NewSrcNote(cx, cg, SRC_WHILE); if (noteIndex < 0) return JS_FALSE; beq = EmitJump(cx, cg, JSOP_IFEQ, 0); if (beq < 0) return JS_FALSE; #if JS_HAS_DESTRUCTURING if (pn3->pn_type == TOK_RB || pn3->pn_type == TOK_RC) { if (!EmitDestructuringOps(cx, cg, op, pn3)) return JS_FALSE; if (js_Emit1(cx, cg, JSOP_POP) < 0) return JS_FALSE; break; } #endif #if JS_HAS_LVALUE_RETURN if (pn3->pn_type == TOK_LP) { JS_ASSERT(pn3->pn_op == JSOP_SETCALL); if (!js_EmitTree(cx, cg, pn3)) return JS_FALSE; if (!js_Emit1(cx, cg, JSOP_ENUMELEM)) return JS_FALSE; break; } #endif #if JS_HAS_XML_SUPPORT if (pn3->pn_type == TOK_UNARYOP) { JS_ASSERT(pn3->pn_op == JSOP_BINDXMLNAME); if (!js_EmitTree(cx, cg, pn3)) return JS_FALSE; if (!js_Emit1(cx, cg, JSOP_ENUMELEM)) return JS_FALSE; break; } #endif /* Now that we're safely past the IFEQ, commit side effects. */ if (!EmitElemOp(cx, pn3, JSOP_ENUMELEM, cg)) return JS_FALSE; break; default: JS_ASSERT(0); } if (emitIFEQ) { /* Annotate so the decompiler can find the loop-closing jump. */ noteIndex = js_NewSrcNote(cx, cg, SRC_WHILE); if (noteIndex < 0) return JS_FALSE; /* Pop and test the loop condition generated by JSOP_FOR*. */ beq = EmitJump(cx, cg, JSOP_IFEQ, 0); if (beq < 0) return JS_FALSE; } } else { op = JSOP_POP; if (!pn2->pn_kid1) { /* No initializer: emit an annotated nop for the decompiler. */ op = JSOP_NOP; } else { cg->treeContext.flags |= TCF_IN_FOR_INIT; #if JS_HAS_DESTRUCTURING pn3 = pn2->pn_kid1; if (pn3->pn_type == TOK_ASSIGN && !MaybeEmitGroupAssignment(cx, cg, op, pn3, &op)) { return JS_FALSE; } #endif if (op == JSOP_POP) { if (!js_EmitTree(cx, cg, pn3)) return JS_FALSE; if (TOKEN_TYPE_IS_DECL(pn3->pn_type)) { /* * Check whether a destructuring-initialized var decl * was optimized to a group assignment. If so, we do * not need to emit a pop below, so switch to a nop, * just for the decompiler. */ JS_ASSERT(pn3->pn_arity == PN_LIST); if (pn3->pn_extra & PNX_GROUPINIT) op = JSOP_NOP; } } cg->treeContext.flags &= ~TCF_IN_FOR_INIT; } noteIndex = js_NewSrcNote(cx, cg, SRC_FOR); if (noteIndex < 0 || js_Emit1(cx, cg, op) < 0) { return JS_FALSE; } top = CG_OFFSET(cg); SET_STATEMENT_TOP(&stmtInfo, top); if (!pn2->pn_kid2) { /* No loop condition: flag this fact in the source notes. */ if (!js_SetSrcNoteOffset(cx, cg, (uintN)noteIndex, 0, 0)) return JS_FALSE; } else { if (!js_EmitTree(cx, cg, pn2->pn_kid2)) return JS_FALSE; if (!js_SetSrcNoteOffset(cx, cg, (uintN)noteIndex, 0, CG_OFFSET(cg) - top)) { return JS_FALSE; } beq = EmitJump(cx, cg, JSOP_IFEQ, 0); if (beq < 0) return JS_FALSE; } /* Set pn3 (used below) here to avoid spurious gcc warnings. */ pn3 = pn2->pn_kid3; } /* Emit code for the loop body. */ if (!js_EmitTree(cx, cg, pn->pn_right)) return JS_FALSE; if (pn2->pn_type != TOK_IN) { /* Set the second note offset so we can find the update part. */ JS_ASSERT(noteIndex != -1); if (!js_SetSrcNoteOffset(cx, cg, (uintN)noteIndex, 1, CG_OFFSET(cg) - top)) { return JS_FALSE; } if (pn3) { /* Set loop and enclosing "update" offsets, for continue. */ stmt = &stmtInfo; do { stmt->update = CG_OFFSET(cg); } while ((stmt = stmt->down) != NULL && stmt->type == STMT_LABEL); op = JSOP_POP; #if JS_HAS_DESTRUCTURING if (pn3->pn_type == TOK_ASSIGN && !MaybeEmitGroupAssignment(cx, cg, op, pn3, &op)) { return JS_FALSE; } #endif if (op == JSOP_POP) { if (!js_EmitTree(cx, cg, pn3)) return JS_FALSE; if (js_Emit1(cx, cg, op) < 0) return JS_FALSE; } /* Restore the absolute line number for source note readers. */ off = (ptrdiff_t) pn->pn_pos.end.lineno; if (CG_CURRENT_LINE(cg) != (uintN) off) { if (js_NewSrcNote2(cx, cg, SRC_SETLINE, off) < 0) return JS_FALSE; CG_CURRENT_LINE(cg) = (uintN) off; } } /* The third note offset helps us find the loop-closing jump. */ if (!js_SetSrcNoteOffset(cx, cg, (uintN)noteIndex, 2, CG_OFFSET(cg) - top)) { return JS_FALSE; } } /* Emit the loop-closing jump and fixup all jump offsets. */ jmp = EmitJump(cx, cg, JSOP_GOTO, top - CG_OFFSET(cg)); if (jmp < 0) return JS_FALSE; if (beq > 0) CHECK_AND_SET_JUMP_OFFSET_AT(cx, cg, beq); if (pn2->pn_type == TOK_IN) { /* Set the SRC_WHILE note offset so we can find the closing jump. */ JS_ASSERT(noteIndex != -1); if (!js_SetSrcNoteOffset(cx, cg, (uintN)noteIndex, 0, jmp - beq)) return JS_FALSE; } /* Now fixup all breaks and continues (before for/in's JSOP_ENDITER). */ if (!js_PopStatementCG(cx, cg)) return JS_FALSE; if (pn2->pn_type == TOK_IN) { if (js_Emit1(cx, cg, JSOP_ENDITER) < 0) return JS_FALSE; } break; case TOK_BREAK: stmt = cg->treeContext.topStmt; atom = pn->pn_atom; if (atom) { ale = js_IndexAtom(cx, atom, &cg->atomList); if (!ale) return JS_FALSE; while (stmt->type != STMT_LABEL || stmt->atom != atom) stmt = stmt->down; noteType = SRC_BREAK2LABEL; } else { ale = NULL; while (!STMT_IS_LOOP(stmt) && stmt->type != STMT_SWITCH) stmt = stmt->down; noteType = SRC_NULL; } if (EmitGoto(cx, cg, stmt, &stmt->breaks, ale, noteType) < 0) return JS_FALSE; break; case TOK_CONTINUE: stmt = cg->treeContext.topStmt; atom = pn->pn_atom; if (atom) { /* Find the loop statement enclosed by the matching label. */ JSStmtInfo *loop = NULL; ale = js_IndexAtom(cx, atom, &cg->atomList); if (!ale) return JS_FALSE; while (stmt->type != STMT_LABEL || stmt->atom != atom) { if (STMT_IS_LOOP(stmt)) loop = stmt; stmt = stmt->down; } stmt = loop; noteType = SRC_CONT2LABEL; } else { ale = NULL; while (!STMT_IS_LOOP(stmt)) stmt = stmt->down; noteType = SRC_CONTINUE; } if (EmitGoto(cx, cg, stmt, &stmt->continues, ale, noteType) < 0) return JS_FALSE; break; case TOK_WITH: if (!js_EmitTree(cx, cg, pn->pn_left)) return JS_FALSE; js_PushStatement(&cg->treeContext, &stmtInfo, STMT_WITH, CG_OFFSET(cg)); if (js_Emit1(cx, cg, JSOP_ENTERWITH) < 0) return JS_FALSE; if (!js_EmitTree(cx, cg, pn->pn_right)) return JS_FALSE; if (js_Emit1(cx, cg, JSOP_LEAVEWITH) < 0) return JS_FALSE; ok = js_PopStatementCG(cx, cg); break; case TOK_TRY: { ptrdiff_t start, end, catchJump, catchStart, finallyCatch; intN depth; JSParseNode *lastCatch; catchJump = catchStart = finallyCatch = -1; /* * Push stmtInfo to track jumps-over-catches and gosubs-to-finally * for later fixup. * * When a finally block is 'active' (STMT_FINALLY on the treeContext), * non-local jumps (including jumps-over-catches) result in a GOSUB * being written into the bytecode stream and fixed-up later (c.f. * EmitBackPatchOp and BackPatch). */ js_PushStatement(&cg->treeContext, &stmtInfo, pn->pn_kid3 ? STMT_FINALLY : STMT_TRY, CG_OFFSET(cg)); /* * About JSOP_SETSP: an exception can be thrown while the stack is in * an unbalanced state, and this imbalance causes problems with things * like function invocation later on. * * To fix this, we compute the 'balanced' stack depth upon try entry, * and then restore the stack to this depth when we hit the first catch * or finally block. We can't just zero the stack, because things like * for/in and with that are active upon entry to the block keep state * variables on the stack. */ depth = cg->stackDepth; /* Mark try location for decompilation, then emit try block. */ if (js_Emit1(cx, cg, JSOP_TRY) < 0) return JS_FALSE; start = CG_OFFSET(cg); if (!js_EmitTree(cx, cg, pn->pn_kid1)) return JS_FALSE; /* GOSUB to finally, if present. */ if (pn->pn_kid3) { if (js_NewSrcNote(cx, cg, SRC_HIDDEN) < 0) return JS_FALSE; jmp = EmitBackPatchOp(cx, cg, JSOP_BACKPATCH, &GOSUBS(stmtInfo)); if (jmp < 0) return JS_FALSE; /* JSOP_RETSUB pops the return pc-index, balancing the stack. */ cg->stackDepth = depth; } /* Emit (hidden) jump over catch and/or finally. */ if (js_NewSrcNote(cx, cg, SRC_HIDDEN) < 0) return JS_FALSE; jmp = EmitBackPatchOp(cx, cg, JSOP_BACKPATCH, &catchJump); if (jmp < 0) return JS_FALSE; end = CG_OFFSET(cg); /* If this try has a catch block, emit it. */ pn2 = pn->pn_kid2; lastCatch = NULL; if (pn2) { jsint count = 0; /* previous catch block's population */ catchStart = end; /* * The emitted code for a catch block looks like: * * [throwing] only if 2nd+ catch block * [leaveblock] only if 2nd+ catch block * enterblock with SRC_CATCH * exception * [dup] only if catchguard * setlocalpop or destructuring code * [< catchguard code >] if there's a catchguard * [ifeq ] " " * [pop] only if catchguard * < catch block contents > * leaveblock * goto non-local; finally applies * * If there's no catch block without a catchguard, the last * points to rethrow code. This * code will [gosub] to the finally code if appropriate, and is * also used for the catch-all trynote for capturing exceptions * thrown from catch{} blocks. */ for (pn3 = pn2->pn_head; pn3; pn3 = pn3->pn_next) { ptrdiff_t guardJump, catchNote; guardJump = GUARDJUMP(stmtInfo); if (guardJump == -1) { /* Set stack to original depth (see SETSP comment above). */ EMIT_UINT16_IMM_OP(JSOP_SETSP, (jsatomid)depth); cg->stackDepth = depth; } else { /* Fix up and clean up previous catch block. */ CHECK_AND_SET_JUMP_OFFSET_AT(cx, cg, guardJump); /* * Account for the pushed exception object that we still * have after the jumping from the previous guard. */ JS_ASSERT(cg->stackDepth == depth); cg->stackDepth = depth + 1; /* * Move exception back to cx->exception to prepare for * the next catch. We hide [throwing] from the decompiler * since it compensates for the hidden JSOP_DUP at the * start of the previous guarded catch. */ if (js_NewSrcNote(cx, cg, SRC_HIDDEN) < 0 || js_Emit1(cx, cg, JSOP_THROWING) < 0) { return JS_FALSE; } /* * Emit an unbalanced [leaveblock] for the previous catch, * whose block object count is saved below. */ if (js_NewSrcNote(cx, cg, SRC_HIDDEN) < 0) return JS_FALSE; JS_ASSERT(count >= 0); EMIT_UINT16_IMM_OP(JSOP_LEAVEBLOCK, count); } /* * Annotate the JSOP_ENTERBLOCK that's about to be generated * by the call to js_EmitTree immediately below. Save this * source note's index in stmtInfo for use by the TOK_CATCH: * case, where the length of the catch guard is set as the * note's offset. */ catchNote = js_NewSrcNote2(cx, cg, SRC_CATCH, 0); if (catchNote < 0) return JS_FALSE; CATCHNOTE(stmtInfo) = catchNote; /* * Emit the lexical scope and catch body. Save the catch's * block object population via count, for use when targeting * guardJump at the next catch (the guard mismatch case). */ JS_ASSERT(pn3->pn_type == TOK_LEXICALSCOPE); count = OBJ_BLOCK_COUNT(cx, ATOM_TO_OBJECT(pn3->pn_atom)); if (!js_EmitTree(cx, cg, pn3)) return JS_FALSE; /* gosub , if required */ if (pn->pn_kid3) { jmp = EmitBackPatchOp(cx, cg, JSOP_BACKPATCH, &GOSUBS(stmtInfo)); if (jmp < 0) return JS_FALSE; JS_ASSERT(cg->stackDepth == depth); } /* * Jump over the remaining catch blocks. This will get fixed * up to jump to after catch/finally. */ if (js_NewSrcNote(cx, cg, SRC_HIDDEN) < 0) return JS_FALSE; jmp = EmitBackPatchOp(cx, cg, JSOP_BACKPATCH, &catchJump); if (jmp < 0) return JS_FALSE; /* * Save a pointer to the last catch node to handle try-finally * and try-catch(guard)-finally special cases. */ lastCatch = pn3->pn_expr; } } /* * Last catch guard jumps to the rethrow code sequence if none of the * guards match. Target guardJump at the beginning of the rethrow * sequence, just in case a guard expression throws and leaves the * stack unbalanced. */ if (lastCatch && lastCatch->pn_kid2) { CHECK_AND_SET_JUMP_OFFSET_AT(cx, cg, GUARDJUMP(stmtInfo)); /* Sync the stack to take into account pushed exception. */ JS_ASSERT(cg->stackDepth == depth); cg->stackDepth = depth + 1; /* * Rethrow the exception, delegating executing of finally if any * to the exception handler. */ if (js_NewSrcNote(cx, cg, SRC_HIDDEN) < 0 || js_Emit1(cx, cg, JSOP_THROW) < 0) { return JS_FALSE; } } JS_ASSERT(cg->stackDepth == depth); /* Emit finally handler if any. */ if (pn->pn_kid3) { /* * We emit [setsp][gosub] to call try-finally when an exception is * thrown from try or try-catch blocks. The [gosub] and [retsub] * opcodes will take care of stacking and rethrowing any exception * pending across the finally. */ finallyCatch = CG_OFFSET(cg); EMIT_UINT16_IMM_OP(JSOP_SETSP, (jsatomid)depth); jmp = EmitBackPatchOp(cx, cg, JSOP_BACKPATCH, &GOSUBS(stmtInfo)); if (jmp < 0) return JS_FALSE; JS_ASSERT(cg->stackDepth == depth); JS_ASSERT((uintN)depth <= cg->maxStackDepth); /* * Fix up the gosubs that might have been emitted before non-local * jumps to the finally code. */ if (!BackPatch(cx, cg, GOSUBS(stmtInfo), CG_NEXT(cg), JSOP_GOSUB)) return JS_FALSE; /* * The stack budget must be balanced at this point. All [gosub] * calls emitted before this point will push two stack slots, one * for the pending exception (or JSVAL_HOLE if there is no pending * exception) and one for the [retsub] pc-index. */ JS_ASSERT(cg->stackDepth == depth); cg->stackDepth += 2; if ((uintN)cg->stackDepth > cg->maxStackDepth) cg->maxStackDepth = cg->stackDepth; /* Now indicate that we're emitting a subroutine body. */ stmtInfo.type = STMT_SUBROUTINE; if (!UpdateLineNumberNotes(cx, cg, pn->pn_kid3)) return JS_FALSE; if (js_Emit1(cx, cg, JSOP_FINALLY) < 0 || !js_EmitTree(cx, cg, pn->pn_kid3) || js_Emit1(cx, cg, JSOP_RETSUB) < 0) { return JS_FALSE; } /* Restore stack depth budget to its balanced state. */ JS_ASSERT(cg->stackDepth == depth + 2); cg->stackDepth = depth; } if (!js_PopStatementCG(cx, cg)) return JS_FALSE; if (js_NewSrcNote(cx, cg, SRC_ENDBRACE) < 0 || js_Emit1(cx, cg, JSOP_NOP) < 0) { return JS_FALSE; } /* Fix up the end-of-try/catch jumps to come here. */ if (!BackPatch(cx, cg, catchJump, CG_NEXT(cg), JSOP_GOTO)) return JS_FALSE; /* * Add the try note last, to let post-order give us the right ordering * (first to last for a given nesting level, inner to outer by level). */ if (pn->pn_kid2) { JS_ASSERT(end != -1 && catchStart != -1); if (!js_NewTryNote(cx, cg, start, end, catchStart)) return JS_FALSE; } /* * If we've got a finally, mark try+catch region with additional * trynote to catch exceptions (re)thrown from a catch block or * for the try{}finally{} case. */ if (pn->pn_kid3) { JS_ASSERT(finallyCatch != -1); if (!js_NewTryNote(cx, cg, start, finallyCatch, finallyCatch)) return JS_FALSE; } break; } case TOK_CATCH: { ptrdiff_t catchStart, guardJump; /* * Morph STMT_BLOCK to STMT_CATCH, note the block entry code offset, * and save the block object atom. */ stmt = cg->treeContext.topStmt; JS_ASSERT(stmt->type == STMT_BLOCK && (stmt->flags & SIF_SCOPE)); stmt->type = STMT_CATCH; catchStart = stmt->update; atom = stmt->atom; /* Go up one statement info record to the TRY or FINALLY record. */ stmt = stmt->down; JS_ASSERT(stmt->type == STMT_TRY || stmt->type == STMT_FINALLY); /* Pick up the pending exception and bind it to the catch variable. */ if (js_Emit1(cx, cg, JSOP_EXCEPTION) < 0) return JS_FALSE; /* * Dup the exception object if there is a guard for rethrowing to use * it later when rethrowing or in other catches. */ if (pn->pn_kid2) { if (js_NewSrcNote(cx, cg, SRC_HIDDEN) < 0 || js_Emit1(cx, cg, JSOP_DUP) < 0) { return JS_FALSE; } } pn2 = pn->pn_kid1; switch (pn2->pn_type) { #if JS_HAS_DESTRUCTURING case TOK_RB: case TOK_RC: if (!EmitDestructuringOps(cx, cg, JSOP_NOP, pn2)) return JS_FALSE; if (js_Emit1(cx, cg, JSOP_POP) < 0) return JS_FALSE; break; #endif case TOK_NAME: /* Inline BindNameToSlot, adding block depth to pn2->pn_slot. */ pn2->pn_slot += OBJ_BLOCK_DEPTH(cx, ATOM_TO_OBJECT(atom)); EMIT_UINT16_IMM_OP(JSOP_SETLOCALPOP, pn2->pn_slot); break; default: JS_ASSERT(0); } /* Emit the guard expression, if there is one. */ if (pn->pn_kid2) { if (!js_EmitTree(cx, cg, pn->pn_kid2)) return JS_FALSE; if (!js_SetSrcNoteOffset(cx, cg, CATCHNOTE(*stmt), 0, CG_OFFSET(cg) - catchStart)) { return JS_FALSE; } /* ifeq */ guardJump = EmitJump(cx, cg, JSOP_IFEQ, 0); if (guardJump < 0) return JS_FALSE; GUARDJUMP(*stmt) = guardJump; /* Pop duplicated exception object as we no longer need it. */ if (js_NewSrcNote(cx, cg, SRC_HIDDEN) < 0 || js_Emit1(cx, cg, JSOP_POP) < 0) { return JS_FALSE; } } /* Emit the catch body. */ if (!js_EmitTree(cx, cg, pn->pn_kid3)) return JS_FALSE; /* * Annotate the JSOP_LEAVEBLOCK that will be emitted as we unwind via * our TOK_LEXICALSCOPE parent, so the decompiler knows to pop. */ off = cg->stackDepth; if (js_NewSrcNote2(cx, cg, SRC_CATCH, off) < 0) return JS_FALSE; break; } case TOK_VAR: if (!EmitVariables(cx, cg, pn, JS_FALSE, ¬eIndex)) return JS_FALSE; break; case TOK_RETURN: /* Push a return value */ pn2 = pn->pn_kid; if (pn2) { if (!js_EmitTree(cx, cg, pn2)) return JS_FALSE; } else { if (js_Emit1(cx, cg, JSOP_PUSH) < 0) return JS_FALSE; } /* * EmitNonLocalJumpFixup mutates op to JSOP_RETRVAL after emitting a * JSOP_SETRVAL if there are open try blocks having finally clauses. * We can't simply transfer control flow to our caller in that case, * because we must gosub to those clauses from inner to outer, with * the correct stack pointer (i.e., after popping any with, for/in, * etc., slots nested inside the finally's try). */ op = JSOP_RETURN; if (!EmitNonLocalJumpFixup(cx, cg, NULL, &op)) return JS_FALSE; if (js_Emit1(cx, cg, op) < 0) return JS_FALSE; break; #if JS_HAS_GENERATORS case TOK_YIELD: if (pn->pn_kid) { if (!js_EmitTree(cx, cg, pn->pn_kid)) return JS_FALSE; } else { if (js_Emit1(cx, cg, JSOP_PUSH) < 0) return JS_FALSE; } if (js_Emit1(cx, cg, JSOP_YIELD) < 0) return JS_FALSE; break; #endif case TOK_LC: #if JS_HAS_XML_SUPPORT if (pn->pn_arity == PN_UNARY) { if (!js_EmitTree(cx, cg, pn->pn_kid)) return JS_FALSE; if (js_Emit1(cx, cg, pn->pn_op) < 0) return JS_FALSE; break; } #endif JS_ASSERT(pn->pn_arity == PN_LIST); noteIndex = -1; tmp = CG_OFFSET(cg); if (pn->pn_extra & PNX_NEEDBRACES) { noteIndex = js_NewSrcNote2(cx, cg, SRC_BRACE, 0); if (noteIndex < 0 || js_Emit1(cx, cg, JSOP_NOP) < 0) return JS_FALSE; } js_PushStatement(&cg->treeContext, &stmtInfo, STMT_BLOCK, top); for (pn2 = pn->pn_head; pn2; pn2 = pn2->pn_next) { if (!js_EmitTree(cx, cg, pn2)) return JS_FALSE; } if (noteIndex >= 0 && !js_SetSrcNoteOffset(cx, cg, (uintN)noteIndex, 0, CG_OFFSET(cg) - tmp)) { return JS_FALSE; } ok = js_PopStatementCG(cx, cg); break; case TOK_BODY: JS_ASSERT(pn->pn_arity == PN_LIST); js_PushStatement(&cg->treeContext, &stmtInfo, STMT_BODY, top); for (pn2 = pn->pn_head; pn2; pn2 = pn2->pn_next) { if (!js_EmitTree(cx, cg, pn2)) return JS_FALSE; } ok = js_PopStatementCG(cx, cg); break; case TOK_SEMI: pn2 = pn->pn_kid; if (pn2) { /* * Top-level or called-from-a-native JS_Execute/EvaluateScript, * debugger, and eval frames may need the value of the ultimate * expression statement as the script's result, despite the fact * that it appears useless to the compiler. */ useful = wantval = !cx->fp->fun || !FUN_INTERPRETED(cx->fp->fun) || (cx->fp->flags & JSFRAME_SPECIAL); if (!useful) { if (!CheckSideEffects(cx, &cg->treeContext, pn2, &useful)) return JS_FALSE; } /* * Don't eliminate apparently useless expressions if they are * labeled expression statements. The tc->topStmt->update test * catches the case where we are nesting in js_EmitTree for a * labeled compound statement. */ if (!useful && (!cg->treeContext.topStmt || cg->treeContext.topStmt->type != STMT_LABEL || cg->treeContext.topStmt->update < CG_OFFSET(cg))) { CG_CURRENT_LINE(cg) = pn2->pn_pos.begin.lineno; if (!js_ReportCompileErrorNumber(cx, cg, JSREPORT_CG | JSREPORT_WARNING | JSREPORT_STRICT, JSMSG_USELESS_EXPR)) { return JS_FALSE; } } else { op = wantval ? JSOP_POPV : JSOP_POP; #if JS_HAS_DESTRUCTURING if (!wantval && pn2->pn_type == TOK_ASSIGN && !MaybeEmitGroupAssignment(cx, cg, op, pn2, &op)) { return JS_FALSE; } #endif if (op != JSOP_NOP) { if (!js_EmitTree(cx, cg, pn2)) return JS_FALSE; if (js_Emit1(cx, cg, op) < 0) return JS_FALSE; } } } break; case TOK_COLON: /* Emit an annotated nop so we know to decompile a label. */ atom = pn->pn_atom; ale = js_IndexAtom(cx, atom, &cg->atomList); if (!ale) return JS_FALSE; pn2 = pn->pn_expr; noteType = (pn2->pn_type == TOK_LC || (pn2->pn_type == TOK_LEXICALSCOPE && pn2->pn_expr->pn_type == TOK_LC)) ? SRC_LABELBRACE : SRC_LABEL; noteIndex = js_NewSrcNote2(cx, cg, noteType, (ptrdiff_t) ALE_INDEX(ale)); if (noteIndex < 0 || js_Emit1(cx, cg, JSOP_NOP) < 0) { return JS_FALSE; } /* Emit code for the labeled statement. */ js_PushStatement(&cg->treeContext, &stmtInfo, STMT_LABEL, CG_OFFSET(cg)); stmtInfo.atom = atom; if (!js_EmitTree(cx, cg, pn2)) return JS_FALSE; if (!js_PopStatementCG(cx, cg)) return JS_FALSE; /* If the statement was compound, emit a note for the end brace. */ if (noteType == SRC_LABELBRACE) { if (js_NewSrcNote(cx, cg, SRC_ENDBRACE) < 0 || js_Emit1(cx, cg, JSOP_NOP) < 0) { return JS_FALSE; } } break; case TOK_COMMA: /* * Emit SRC_PCDELTA notes on each JSOP_POP between comma operands. * These notes help the decompiler bracket the bytecodes generated * from each sub-expression that follows a comma. */ off = noteIndex = -1; for (pn2 = pn->pn_head; ; pn2 = pn2->pn_next) { if (!js_EmitTree(cx, cg, pn2)) return JS_FALSE; tmp = CG_OFFSET(cg); if (noteIndex >= 0) { if (!js_SetSrcNoteOffset(cx, cg, (uintN)noteIndex, 0, tmp-off)) return JS_FALSE; } if (!pn2->pn_next) break; off = tmp; noteIndex = js_NewSrcNote2(cx, cg, SRC_PCDELTA, 0); if (noteIndex < 0 || js_Emit1(cx, cg, JSOP_POP) < 0) { return JS_FALSE; } } break; case TOK_ASSIGN: /* * Check left operand type and generate specialized code for it. * Specialize to avoid ECMA "reference type" values on the operand * stack, which impose pervasive runtime "GetValue" costs. */ pn2 = pn->pn_left; JS_ASSERT(pn2->pn_type != TOK_RP); atomIndex = (jsatomid) -1; switch (pn2->pn_type) { case TOK_NAME: if (!BindNameToSlot(cx, &cg->treeContext, pn2, JS_FALSE)) return JS_FALSE; if (pn2->pn_slot >= 0) { atomIndex = (jsatomid) pn2->pn_slot; } else { ale = js_IndexAtom(cx, pn2->pn_atom, &cg->atomList); if (!ale) return JS_FALSE; atomIndex = ALE_INDEX(ale); EMIT_ATOM_INDEX_OP(JSOP_BINDNAME, atomIndex); } break; case TOK_DOT: if (!js_EmitTree(cx, cg, pn2->pn_expr)) return JS_FALSE; ale = js_IndexAtom(cx, pn2->pn_atom, &cg->atomList); if (!ale) return JS_FALSE; atomIndex = ALE_INDEX(ale); break; case TOK_LB: JS_ASSERT(pn2->pn_arity == PN_BINARY); if (!js_EmitTree(cx, cg, pn2->pn_left)) return JS_FALSE; if (!js_EmitTree(cx, cg, pn2->pn_right)) return JS_FALSE; break; #if JS_HAS_DESTRUCTURING case TOK_RB: case TOK_RC: break; #endif #if JS_HAS_LVALUE_RETURN case TOK_LP: if (!js_EmitTree(cx, cg, pn2)) return JS_FALSE; break; #endif #if JS_HAS_XML_SUPPORT case TOK_UNARYOP: JS_ASSERT(pn2->pn_op == JSOP_SETXMLNAME); if (!js_EmitTree(cx, cg, pn2->pn_kid)) return JS_FALSE; if (js_Emit1(cx, cg, JSOP_BINDXMLNAME) < 0) return JS_FALSE; break; #endif default: JS_ASSERT(0); } op = pn->pn_op; #if JS_HAS_GETTER_SETTER if (op == JSOP_GETTER || op == JSOP_SETTER) { /* We'll emit these prefix bytecodes after emitting the r.h.s. */ if (atomIndex != (jsatomid) -1 && atomIndex >= JS_BIT(16)) { ReportStatementTooLarge(cx, cg); return JS_FALSE; } } else #endif /* If += or similar, dup the left operand and get its value. */ if (op != JSOP_NOP) { switch (pn2->pn_type) { case TOK_NAME: if (pn2->pn_op != JSOP_SETNAME) { EMIT_UINT16_IMM_OP((pn2->pn_op == JSOP_SETGVAR) ? JSOP_GETGVAR : (pn2->pn_op == JSOP_SETARG) ? JSOP_GETARG : (pn2->pn_op == JSOP_SETLOCAL) ? JSOP_GETLOCAL : JSOP_GETVAR, atomIndex); break; } /* FALL THROUGH */ case TOK_DOT: if (js_Emit1(cx, cg, JSOP_DUP) < 0) return JS_FALSE; EMIT_ATOM_INDEX_OP((pn2->pn_type == TOK_NAME) ? JSOP_GETXPROP : JSOP_GETPROP, atomIndex); break; case TOK_LB: #if JS_HAS_LVALUE_RETURN case TOK_LP: #endif #if JS_HAS_XML_SUPPORT case TOK_UNARYOP: #endif if (js_Emit1(cx, cg, JSOP_DUP2) < 0) return JS_FALSE; if (js_Emit1(cx, cg, JSOP_GETELEM) < 0) return JS_FALSE; break; default:; } } /* Now emit the right operand (it may affect the namespace). */ if (!js_EmitTree(cx, cg, pn->pn_right)) return JS_FALSE; /* If += etc., emit the binary operator with a decompiler note. */ if (op != JSOP_NOP) { /* * Take care to avoid SRC_ASSIGNOP if the left-hand side is a * const declared in a function (i.e., with non-negative pn_slot * and JSPROP_READONLY in pn_attrs), as in this case (just a bit * further below) we will avoid emitting the assignment op. */ if (pn2->pn_type != TOK_NAME || pn2->pn_slot < 0 || !(pn2->pn_attrs & JSPROP_READONLY)) { if (js_NewSrcNote(cx, cg, SRC_ASSIGNOP) < 0) return JS_FALSE; } if (js_Emit1(cx, cg, op) < 0) return JS_FALSE; } /* Left parts such as a.b.c and a[b].c need a decompiler note. */ if (pn2->pn_type != TOK_NAME && #if JS_HAS_DESTRUCTURING pn2->pn_type != TOK_RB && pn2->pn_type != TOK_RC && #endif js_NewSrcNote2(cx, cg, SrcNoteForPropOp(pn2, pn2->pn_op), CG_OFFSET(cg) - top) < 0) { return JS_FALSE; } /* Finally, emit the specialized assignment bytecode. */ switch (pn2->pn_type) { case TOK_NAME: if (pn2->pn_slot < 0 || !(pn2->pn_attrs & JSPROP_READONLY)) { if (pn2->pn_slot >= 0) { EMIT_UINT16_IMM_OP(pn2->pn_op, atomIndex); } else { case TOK_DOT: EMIT_ATOM_INDEX_OP(pn2->pn_op, atomIndex); } } break; case TOK_LB: #if JS_HAS_LVALUE_RETURN case TOK_LP: #endif if (js_Emit1(cx, cg, JSOP_SETELEM) < 0) return JS_FALSE; break; #if JS_HAS_DESTRUCTURING case TOK_RB: case TOK_RC: if (!EmitDestructuringOps(cx, cg, JSOP_SETNAME, pn2)) return JS_FALSE; break; #endif #if JS_HAS_XML_SUPPORT case TOK_UNARYOP: if (js_Emit1(cx, cg, JSOP_SETXMLNAME) < 0) return JS_FALSE; break; #endif default: JS_ASSERT(0); } break; case TOK_HOOK: /* Emit the condition, then branch if false to the else part. */ if (!js_EmitTree(cx, cg, pn->pn_kid1)) return JS_FALSE; noteIndex = js_NewSrcNote(cx, cg, SRC_COND); if (noteIndex < 0) return JS_FALSE; beq = EmitJump(cx, cg, JSOP_IFEQ, 0); if (beq < 0 || !js_EmitTree(cx, cg, pn->pn_kid2)) return JS_FALSE; /* Jump around else, fixup the branch, emit else, fixup jump. */ jmp = EmitJump(cx, cg, JSOP_GOTO, 0); if (jmp < 0) return JS_FALSE; CHECK_AND_SET_JUMP_OFFSET_AT(cx, cg, beq); /* * Because each branch pushes a single value, but our stack budgeting * analysis ignores branches, we now have to adjust cg->stackDepth to * ignore the value pushed by the first branch. Execution will follow * only one path, so we must decrement cg->stackDepth. * * Failing to do this will foil code, such as the try/catch/finally * exception handling code generator, that samples cg->stackDepth for * use at runtime (JSOP_SETSP), or in let expression and block code * generation, which must use the stack depth to compute local stack * indexes correctly. */ JS_ASSERT(cg->stackDepth > 0); cg->stackDepth--; if (!js_EmitTree(cx, cg, pn->pn_kid3)) return JS_FALSE; CHECK_AND_SET_JUMP_OFFSET_AT(cx, cg, jmp); if (!js_SetSrcNoteOffset(cx, cg, noteIndex, 0, jmp - beq)) return JS_FALSE; break; case TOK_OR: case TOK_AND: /* * JSOP_OR converts the operand on the stack to boolean, and if true, * leaves the original operand value on the stack and jumps; otherwise * it pops and falls into the next bytecode, which evaluates the right * operand. The jump goes around the right operand evaluation. * * JSOP_AND converts the operand on the stack to boolean, and if false, * leaves the original operand value on the stack and jumps; otherwise * it pops and falls into the right operand's bytecode. * * Avoid tail recursion for long ||...|| expressions and long &&...&& * expressions or long mixtures of ||'s and &&'s that can easily blow * the stack, by forward-linking and then backpatching all the JSOP_OR * and JSOP_AND bytecodes' immediate jump-offset operands. */ pn3 = pn; if (!js_EmitTree(cx, cg, pn->pn_left)) return JS_FALSE; top = EmitJump(cx, cg, JSOP_BACKPATCH_POP, 0); if (top < 0) return JS_FALSE; jmp = top; pn2 = pn->pn_right; while (pn2->pn_type == TOK_OR || pn2->pn_type == TOK_AND) { pn = pn2; if (!js_EmitTree(cx, cg, pn->pn_left)) return JS_FALSE; off = EmitJump(cx, cg, JSOP_BACKPATCH_POP, 0); if (off < 0) return JS_FALSE; if (!SetBackPatchDelta(cx, cg, CG_CODE(cg, jmp), off - jmp)) return JS_FALSE; jmp = off; pn2 = pn->pn_right; } if (!js_EmitTree(cx, cg, pn2)) return JS_FALSE; off = CG_OFFSET(cg); do { pc = CG_CODE(cg, top); tmp = GetJumpOffset(cg, pc); CHECK_AND_SET_JUMP_OFFSET(cx, cg, pc, off - top); *pc = pn3->pn_op; top += tmp; } while ((pn3 = pn3->pn_right) != pn2); break; case TOK_BITOR: case TOK_BITXOR: case TOK_BITAND: case TOK_EQOP: case TOK_RELOP: case TOK_IN: case TOK_INSTANCEOF: case TOK_SHOP: case TOK_PLUS: case TOK_MINUS: case TOK_STAR: case TOK_DIVOP: if (pn->pn_arity == PN_LIST) { /* Left-associative operator chain: avoid too much recursion. */ pn2 = pn->pn_head; if (!js_EmitTree(cx, cg, pn2)) return JS_FALSE; op = pn->pn_op; while ((pn2 = pn2->pn_next) != NULL) { if (!js_EmitTree(cx, cg, pn2)) return JS_FALSE; if (js_Emit1(cx, cg, op) < 0) return JS_FALSE; } } else { #if JS_HAS_XML_SUPPORT uintN oldflags; case TOK_DBLCOLON: if (pn->pn_arity == PN_NAME) { if (!js_EmitTree(cx, cg, pn->pn_expr)) return JS_FALSE; if (!EmitAtomOp(cx, pn, pn->pn_op, cg)) return JS_FALSE; break; } /* * Binary :: has a right operand that brackets arbitrary code, * possibly including a let (a = b) ... expression. We must clear * TCF_IN_FOR_INIT to avoid mis-compiling such beasts. */ oldflags = cg->treeContext.flags; cg->treeContext.flags &= ~TCF_IN_FOR_INIT; #endif /* Binary operators that evaluate both operands unconditionally. */ if (!js_EmitTree(cx, cg, pn->pn_left)) return JS_FALSE; if (!js_EmitTree(cx, cg, pn->pn_right)) return JS_FALSE; #if JS_HAS_XML_SUPPORT cg->treeContext.flags |= oldflags & TCF_IN_FOR_INIT; #endif if (js_Emit1(cx, cg, pn->pn_op) < 0) return JS_FALSE; } break; case TOK_THROW: #if JS_HAS_XML_SUPPORT case TOK_AT: case TOK_DEFAULT: JS_ASSERT(pn->pn_arity == PN_UNARY); /* FALL THROUGH */ #endif case TOK_UNARYOP: { uintN oldflags; /* Unary op, including unary +/-. */ pn2 = pn->pn_kid; op = pn->pn_op; if (op == JSOP_TYPEOF) { for (pn3 = pn2; pn3->pn_type == TOK_RP; pn3 = pn3->pn_kid) continue; if (pn3->pn_type != TOK_NAME) op = JSOP_TYPEOFEXPR; } oldflags = cg->treeContext.flags; cg->treeContext.flags &= ~TCF_IN_FOR_INIT; if (!js_EmitTree(cx, cg, pn2)) return JS_FALSE; cg->treeContext.flags |= oldflags & TCF_IN_FOR_INIT; #if JS_HAS_XML_SUPPORT if (op == JSOP_XMLNAME && js_NewSrcNote2(cx, cg, SRC_PCBASE, CG_OFFSET(cg) - pn2->pn_offset) < 0) { return JS_FALSE; } #endif if (js_Emit1(cx, cg, op) < 0) return JS_FALSE; break; } case TOK_INC: case TOK_DEC: { intN depth; /* Emit lvalue-specialized code for ++/-- operators. */ pn2 = pn->pn_kid; JS_ASSERT(pn2->pn_type != TOK_RP); op = pn->pn_op; depth = cg->stackDepth; switch (pn2->pn_type) { case TOK_NAME: pn2->pn_op = op; if (!BindNameToSlot(cx, &cg->treeContext, pn2, JS_FALSE)) return JS_FALSE; op = pn2->pn_op; if (pn2->pn_slot >= 0) { if (pn2->pn_attrs & JSPROP_READONLY) { /* Incrementing a declared const: just get its value. */ op = ((js_CodeSpec[op].format & JOF_TYPEMASK) == JOF_CONST) ? JSOP_GETGVAR : JSOP_GETVAR; } atomIndex = (jsatomid) pn2->pn_slot; EMIT_UINT16_IMM_OP(op, atomIndex); } else { if (!EmitAtomOp(cx, pn2, op, cg)) return JS_FALSE; } break; case TOK_DOT: if (!EmitPropOp(cx, pn2, op, cg)) return JS_FALSE; ++depth; break; case TOK_LB: if (!EmitElemOp(cx, pn2, op, cg)) return JS_FALSE; depth += 2; break; #if JS_HAS_LVALUE_RETURN case TOK_LP: if (!js_EmitTree(cx, cg, pn2)) return JS_FALSE; depth = cg->stackDepth; if (js_NewSrcNote2(cx, cg, SRC_PCBASE, CG_OFFSET(cg) - pn2->pn_offset) < 0) { return JS_FALSE; } if (js_Emit1(cx, cg, op) < 0) return JS_FALSE; break; #endif #if JS_HAS_XML_SUPPORT case TOK_UNARYOP: JS_ASSERT(pn2->pn_op == JSOP_SETXMLNAME); if (!js_EmitTree(cx, cg, pn2->pn_kid)) return JS_FALSE; if (js_Emit1(cx, cg, JSOP_BINDXMLNAME) < 0) return JS_FALSE; depth = cg->stackDepth; if (js_Emit1(cx, cg, op) < 0) return JS_FALSE; break; #endif default: JS_ASSERT(0); } /* * Allocate another stack slot for GC protection in case the initial * value being post-incremented or -decremented is not a number, but * converts to a jsdouble. In the TOK_NAME cases, op has 0 operand * uses and 1 definition, so we don't need an extra stack slot -- we * can use the one allocated for the def. */ if (pn2->pn_type != TOK_NAME && (js_CodeSpec[op].format & JOF_POST) && (uintN)depth == cg->maxStackDepth) { ++cg->maxStackDepth; } break; } case TOK_DELETE: /* * Under ECMA 3, deleting a non-reference returns true -- but alas we * must evaluate the operand if it appears it might have side effects. */ pn2 = pn->pn_kid; switch (pn2->pn_type) { case TOK_NAME: pn2->pn_op = JSOP_DELNAME; if (!BindNameToSlot(cx, &cg->treeContext, pn2, JS_FALSE)) return JS_FALSE; op = pn2->pn_op; if (op == JSOP_FALSE) { if (js_Emit1(cx, cg, op) < 0) return JS_FALSE; } else { if (!EmitAtomOp(cx, pn2, op, cg)) return JS_FALSE; } break; case TOK_DOT: if (!EmitPropOp(cx, pn2, JSOP_DELPROP, cg)) return JS_FALSE; break; #if JS_HAS_XML_SUPPORT case TOK_DBLDOT: if (!EmitElemOp(cx, pn2, JSOP_DELDESC, cg)) return JS_FALSE; break; #endif #if JS_HAS_LVALUE_RETURN case TOK_LP: if (pn2->pn_op != JSOP_SETCALL) { JS_ASSERT(pn2->pn_op == JSOP_CALL || pn2->pn_op == JSOP_EVAL); pn2->pn_op = JSOP_SETCALL; } top = CG_OFFSET(cg); if (!js_EmitTree(cx, cg, pn2)) return JS_FALSE; if (js_NewSrcNote2(cx, cg, SRC_PCBASE, CG_OFFSET(cg) - top) < 0) return JS_FALSE; if (js_Emit1(cx, cg, JSOP_DELELEM) < 0) return JS_FALSE; break; #endif case TOK_LB: if (!EmitElemOp(cx, pn2, JSOP_DELELEM, cg)) return JS_FALSE; break; default: /* * If useless, just emit JSOP_TRUE; otherwise convert delete foo() * to foo(), true (a comma expression, requiring SRC_PCDELTA). */ useful = JS_FALSE; if (!CheckSideEffects(cx, &cg->treeContext, pn2, &useful)) return JS_FALSE; if (!useful) { off = noteIndex = -1; } else { if (!js_EmitTree(cx, cg, pn2)) return JS_FALSE; off = CG_OFFSET(cg); noteIndex = js_NewSrcNote2(cx, cg, SRC_PCDELTA, 0); if (noteIndex < 0 || js_Emit1(cx, cg, JSOP_POP) < 0) return JS_FALSE; } if (js_Emit1(cx, cg, JSOP_TRUE) < 0) return JS_FALSE; if (noteIndex >= 0) { tmp = CG_OFFSET(cg); if (!js_SetSrcNoteOffset(cx, cg, (uintN)noteIndex, 0, tmp-off)) return JS_FALSE; } } break; #if JS_HAS_XML_SUPPORT case TOK_FILTER: if (!js_EmitTree(cx, cg, pn->pn_left)) return JS_FALSE; jmp = js_Emit3(cx, cg, JSOP_FILTER, 0, 0); if (jmp < 0) return JS_FALSE; if (!js_EmitTree(cx, cg, pn->pn_right)) return JS_FALSE; if (js_Emit1(cx, cg, JSOP_ENDFILTER) < 0) return JS_FALSE; CHECK_AND_SET_JUMP_OFFSET_AT(cx, cg, jmp); break; #endif case TOK_DOT: /* * Pop a stack operand, convert it to object, get a property named by * this bytecode's immediate-indexed atom operand, and push its value * (not a reference to it). This bytecode sets the virtual machine's * "obj" register to the left operand's ToObject conversion result, * for use by JSOP_PUSHOBJ. */ ok = EmitPropOp(cx, pn, pn->pn_op, cg); break; case TOK_LB: #if JS_HAS_XML_SUPPORT case TOK_DBLDOT: #endif /* * Pop two operands, convert the left one to object and the right one * to property name (atom or tagged int), get the named property, and * push its value. Set the "obj" register to the result of ToObject * on the left operand. */ ok = EmitElemOp(cx, pn, pn->pn_op, cg); break; case TOK_NEW: case TOK_LP: { uintN oldflags; /* * Emit function call or operator new (constructor call) code. * First, emit code for the left operand to evaluate the callable or * constructable object expression. * * For E4X, if this expression is a dotted member reference, select * JSOP_GETMETHOD instead of JSOP_GETPROP. ECMA-357 separates XML * method lookup from the normal property id lookup done for native * objects. */ pn2 = pn->pn_head; #if JS_HAS_XML_SUPPORT if (pn2->pn_type == TOK_DOT && pn2->pn_op != JSOP_GETMETHOD) { JS_ASSERT(pn2->pn_op == JSOP_GETPROP); pn2->pn_op = JSOP_GETMETHOD; pn2->pn_attrs |= JSPROP_IMPLICIT_FUNCTION_NAMESPACE; } #endif if (!js_EmitTree(cx, cg, pn2)) return JS_FALSE; /* * Push the virtual machine's "obj" register, which was set by a * name, property, or element get (or set) bytecode. */ if (js_Emit1(cx, cg, JSOP_PUSHOBJ) < 0) return JS_FALSE; /* Remember start of callable-object bytecode for decompilation hint. */ off = top; /* * Emit code for each argument in order, then emit the JSOP_*CALL or * JSOP_NEW bytecode with a two-byte immediate telling how many args * were pushed on the operand stack. */ oldflags = cg->treeContext.flags; cg->treeContext.flags &= ~TCF_IN_FOR_INIT; for (pn2 = pn2->pn_next; pn2; pn2 = pn2->pn_next) { if (!js_EmitTree(cx, cg, pn2)) return JS_FALSE; } cg->treeContext.flags |= oldflags & TCF_IN_FOR_INIT; if (js_NewSrcNote2(cx, cg, SRC_PCBASE, CG_OFFSET(cg) - off) < 0) return JS_FALSE; argc = pn->pn_count - 1; if (js_Emit3(cx, cg, pn->pn_op, ARGC_HI(argc), ARGC_LO(argc)) < 0) return JS_FALSE; break; } case TOK_LEXICALSCOPE: { JSObject *obj; jsint count; atom = pn->pn_atom; obj = ATOM_TO_OBJECT(atom); js_PushBlockScope(&cg->treeContext, &stmtInfo, atom, CG_OFFSET(cg)); OBJ_SET_BLOCK_DEPTH(cx, obj, cg->stackDepth); count = OBJ_BLOCK_COUNT(cx, obj); cg->stackDepth += count; if ((uintN)cg->stackDepth > cg->maxStackDepth) cg->maxStackDepth = cg->stackDepth; /* * If this lexical scope is not for a catch block, let block or let * expression, or any kind of for loop (where the scope starts in the * head after the first part if for (;;), else in the body if for-in); * and if our container is top-level but not a function body, or else * a block statement; then emit a SRC_BRACE note. All other container * statements get braces by default from the decompiler. */ noteIndex = -1; type = pn->pn_expr->pn_type; if (type != TOK_CATCH && type != TOK_LET && type != TOK_FOR && (!(stmt = stmtInfo.down) ? !(cg->treeContext.flags & TCF_IN_FUNCTION) : stmt->type == STMT_BLOCK)) { #if defined DEBUG_brendan || defined DEBUG_mrbkap /* There must be no source note already output for the next op. */ JS_ASSERT(CG_NOTE_COUNT(cg) == 0 || CG_LAST_NOTE_OFFSET(cg) != CG_OFFSET(cg) || !GettableNoteForNextOp(cg)); #endif noteIndex = js_NewSrcNote2(cx, cg, SRC_BRACE, 0); if (noteIndex < 0) return JS_FALSE; } ale = js_IndexAtom(cx, atom, &cg->atomList); if (!ale) return JS_FALSE; JS_ASSERT(CG_OFFSET(cg) == top); EMIT_ATOM_INDEX_OP(JSOP_ENTERBLOCK, ALE_INDEX(ale)); if (!js_EmitTree(cx, cg, pn->pn_expr)) return JS_FALSE; op = pn->pn_op; if (op == JSOP_LEAVEBLOCKEXPR) { if (js_NewSrcNote2(cx, cg, SRC_PCBASE, CG_OFFSET(cg) - top) < 0) return JS_FALSE; } else { if (noteIndex >= 0 && !js_SetSrcNoteOffset(cx, cg, (uintN)noteIndex, 0, CG_OFFSET(cg) - top)) { return JS_FALSE; } } /* Emit the JSOP_LEAVEBLOCK or JSOP_LEAVEBLOCKEXPR opcode. */ EMIT_UINT16_IMM_OP(op, count); cg->stackDepth -= count; ok = js_PopStatementCG(cx, cg); break; } #if JS_HAS_BLOCK_SCOPE case TOK_LET: /* Let statements have their variable declarations on the left. */ if (pn->pn_arity == PN_BINARY) { pn2 = pn->pn_right; pn = pn->pn_left; } else { pn2 = NULL; } /* Non-null pn2 means that pn is the variable list from a let head. */ JS_ASSERT(pn->pn_arity == PN_LIST); if (!EmitVariables(cx, cg, pn, pn2 != NULL, ¬eIndex)) return JS_FALSE; /* Thus non-null pn2 is the body of the let block or expression. */ tmp = CG_OFFSET(cg); if (pn2 && !js_EmitTree(cx, cg, pn2)) return JS_FALSE; if (noteIndex >= 0 && !js_SetSrcNoteOffset(cx, cg, (uintN)noteIndex, 0, CG_OFFSET(cg) - tmp)) { return JS_FALSE; } break; #endif /* JS_HAS_BLOCK_SCOPE */ #if JS_HAS_GENERATORS case TOK_ARRAYPUSH: /* * The array object's stack index is in cg->arrayCompSlot. See below * under the array initialiser code generator for array comprehension * special casing. */ if (!js_EmitTree(cx, cg, pn->pn_kid)) return JS_FALSE; EMIT_UINT16_IMM_OP(pn->pn_op, cg->arrayCompSlot); break; #endif case TOK_RB: #if JS_HAS_GENERATORS case TOK_ARRAYCOMP: #endif /* * Emit code for [a, b, c] of the form: * t = new Array; t[0] = a; t[1] = b; t[2] = c; t; * but use a stack slot for t and avoid dup'ing and popping it via * the JSOP_NEWINIT and JSOP_INITELEM bytecodes. */ ale = js_IndexAtom(cx, CLASS_ATOM(cx, Array), &cg->atomList); if (!ale) return JS_FALSE; EMIT_ATOM_INDEX_OP(JSOP_NAME, ALE_INDEX(ale)); if (js_Emit1(cx, cg, JSOP_PUSHOBJ) < 0) return JS_FALSE; if (js_Emit1(cx, cg, JSOP_NEWINIT) < 0) return JS_FALSE; pn2 = pn->pn_head; #if JS_HAS_SHARP_VARS if (pn2 && pn2->pn_type == TOK_DEFSHARP) { EMIT_UINT16_IMM_OP(JSOP_DEFSHARP, (jsatomid)pn2->pn_num); pn2 = pn2->pn_next; } #endif #if JS_HAS_GENERATORS if (pn->pn_type == TOK_ARRAYCOMP) { uintN saveSlot; /* * Pass the new array's stack index to the TOK_ARRAYPUSH case by * storing it in pn->pn_extra, then simply traverse the TOK_FOR * node and its kids under pn2 to generate this comprehension. */ JS_ASSERT(cg->stackDepth > 0); saveSlot = cg->arrayCompSlot; cg->arrayCompSlot = (uint32) (cg->stackDepth - 1); if (!js_EmitTree(cx, cg, pn2)) return JS_FALSE; cg->arrayCompSlot = saveSlot; /* Emit the usual op needed for decompilation. */ if (js_Emit1(cx, cg, JSOP_ENDINIT) < 0) return JS_FALSE; break; } #endif /* JS_HAS_GENERATORS */ for (atomIndex = 0; pn2; atomIndex++, pn2 = pn2->pn_next) { if (!EmitNumberOp(cx, atomIndex, cg)) return JS_FALSE; /* FIXME 260106: holes in a sparse initializer are void-filled. */ if (pn2->pn_type == TOK_COMMA) { if (js_Emit1(cx, cg, JSOP_PUSH) < 0) return JS_FALSE; } else { if (!js_EmitTree(cx, cg, pn2)) return JS_FALSE; } if (js_Emit1(cx, cg, JSOP_INITELEM) < 0) return JS_FALSE; } if (pn->pn_extra & PNX_ENDCOMMA) { /* Emit a source note so we know to decompile an extra comma. */ if (js_NewSrcNote(cx, cg, SRC_CONTINUE) < 0) return JS_FALSE; } /* Emit an op for sharp array cleanup and decompilation. */ if (js_Emit1(cx, cg, JSOP_ENDINIT) < 0) return JS_FALSE; break; case TOK_RC: /* * Emit code for {p:a, '%q':b, 2:c} of the form: * t = new Object; t.p = a; t['%q'] = b; t[2] = c; t; * but use a stack slot for t and avoid dup'ing and popping it via * the JSOP_NEWINIT and JSOP_INITELEM bytecodes. */ ale = js_IndexAtom(cx, CLASS_ATOM(cx, Object), &cg->atomList); if (!ale) return JS_FALSE; EMIT_ATOM_INDEX_OP(JSOP_NAME, ALE_INDEX(ale)); if (js_Emit1(cx, cg, JSOP_PUSHOBJ) < 0) return JS_FALSE; if (js_Emit1(cx, cg, JSOP_NEWINIT) < 0) return JS_FALSE; pn2 = pn->pn_head; #if JS_HAS_SHARP_VARS if (pn2 && pn2->pn_type == TOK_DEFSHARP) { EMIT_UINT16_IMM_OP(JSOP_DEFSHARP, (jsatomid)pn2->pn_num); pn2 = pn2->pn_next; } #endif for (; pn2; pn2 = pn2->pn_next) { /* Emit an index for t[2], else map an atom for t.p or t['%q']. */ pn3 = pn2->pn_left; switch (pn3->pn_type) { case TOK_NUMBER: if (!EmitNumberOp(cx, pn3->pn_dval, cg)) return JS_FALSE; break; case TOK_NAME: case TOK_STRING: ale = js_IndexAtom(cx, pn3->pn_atom, &cg->atomList); if (!ale) return JS_FALSE; break; default: JS_ASSERT(0); } /* Emit code for the property initializer. */ if (!js_EmitTree(cx, cg, pn2->pn_right)) return JS_FALSE; #if JS_HAS_GETTER_SETTER op = pn2->pn_op; if (op == JSOP_GETTER || op == JSOP_SETTER) { if (pn3->pn_type != TOK_NUMBER && ALE_INDEX(ale) >= JS_BIT(16)) { ReportStatementTooLarge(cx, cg); return JS_FALSE; } if (js_Emit1(cx, cg, op) < 0) return JS_FALSE; } #endif /* Annotate JSOP_INITELEM so we decompile 2:c and not just c. */ if (pn3->pn_type == TOK_NUMBER) { if (js_NewSrcNote(cx, cg, SRC_INITPROP) < 0) return JS_FALSE; if (js_Emit1(cx, cg, JSOP_INITELEM) < 0) return JS_FALSE; } else { EMIT_ATOM_INDEX_OP(JSOP_INITPROP, ALE_INDEX(ale)); } } /* Emit an op for sharpArray cleanup and decompilation. */ if (js_Emit1(cx, cg, JSOP_ENDINIT) < 0) return JS_FALSE; break; #if JS_HAS_SHARP_VARS case TOK_DEFSHARP: if (!js_EmitTree(cx, cg, pn->pn_kid)) return JS_FALSE; EMIT_UINT16_IMM_OP(JSOP_DEFSHARP, (jsatomid) pn->pn_num); break; case TOK_USESHARP: EMIT_UINT16_IMM_OP(JSOP_USESHARP, (jsatomid) pn->pn_num); break; #endif /* JS_HAS_SHARP_VARS */ case TOK_RP: { uintN oldflags; /* * The node for (e) has e as its kid, enabling users who want to nest * assignment expressions in conditions to avoid the error correction * done by Condition (from x = y to x == y) by double-parenthesizing. */ oldflags = cg->treeContext.flags; cg->treeContext.flags &= ~TCF_IN_FOR_INIT; if (!js_EmitTree(cx, cg, pn->pn_kid)) return JS_FALSE; cg->treeContext.flags |= oldflags & TCF_IN_FOR_INIT; if (js_Emit1(cx, cg, JSOP_GROUP) < 0) return JS_FALSE; break; } case TOK_NAME: if (!BindNameToSlot(cx, &cg->treeContext, pn, JS_FALSE)) return JS_FALSE; op = pn->pn_op; if (op == JSOP_ARGUMENTS) { if (js_Emit1(cx, cg, op) < 0) return JS_FALSE; break; } if (pn->pn_slot >= 0) { atomIndex = (jsatomid) pn->pn_slot; EMIT_UINT16_IMM_OP(op, atomIndex); break; } /* FALL THROUGH */ #if JS_HAS_XML_SUPPORT case TOK_XMLATTR: case TOK_XMLSPACE: case TOK_XMLTEXT: case TOK_XMLCDATA: case TOK_XMLCOMMENT: #endif case TOK_STRING: case TOK_OBJECT: /* * The scanner and parser associate JSOP_NAME with TOK_NAME, although * other bytecodes may result instead (JSOP_BINDNAME/JSOP_SETNAME, * JSOP_FORNAME, etc.). Among JSOP_*NAME* variants, only JSOP_NAME * may generate the first operand of a call or new expression, so only * it sets the "obj" virtual machine register to the object along the * scope chain in which the name was found. * * Token types for STRING and OBJECT have corresponding bytecode ops * in pn_op and emit the same format as NAME, so they share this code. */ ok = EmitAtomOp(cx, pn, pn->pn_op, cg); break; case TOK_NUMBER: ok = EmitNumberOp(cx, pn->pn_dval, cg); break; #if JS_HAS_XML_SUPPORT case TOK_ANYNAME: #endif case TOK_PRIMARY: if (js_Emit1(cx, cg, pn->pn_op) < 0) return JS_FALSE; break; #if JS_HAS_DEBUGGER_KEYWORD case TOK_DEBUGGER: if (js_Emit1(cx, cg, JSOP_DEBUGGER) < 0) return JS_FALSE; break; #endif /* JS_HAS_DEBUGGER_KEYWORD */ #if JS_HAS_XML_SUPPORT case TOK_XMLELEM: case TOK_XMLLIST: if (pn->pn_op == JSOP_XMLOBJECT) { ok = EmitAtomOp(cx, pn, pn->pn_op, cg); break; } JS_ASSERT(pn->pn_type == TOK_XMLLIST || pn->pn_count != 0); switch (pn->pn_head ? pn->pn_head->pn_type : TOK_XMLLIST) { case TOK_XMLETAGO: JS_ASSERT(0); /* FALL THROUGH */ case TOK_XMLPTAGC: case TOK_XMLSTAGO: break; default: if (js_Emit1(cx, cg, JSOP_STARTXML) < 0) return JS_FALSE; } for (pn2 = pn->pn_head; pn2; pn2 = pn2->pn_next) { if (pn2->pn_type == TOK_LC && js_Emit1(cx, cg, JSOP_STARTXMLEXPR) < 0) { return JS_FALSE; } if (!js_EmitTree(cx, cg, pn2)) return JS_FALSE; if (pn2 != pn->pn_head && js_Emit1(cx, cg, JSOP_ADD) < 0) return JS_FALSE; } if (pn->pn_extra & PNX_XMLROOT) { if (pn->pn_count == 0) { JS_ASSERT(pn->pn_type == TOK_XMLLIST); atom = cx->runtime->atomState.emptyAtom; ale = js_IndexAtom(cx, atom, &cg->atomList); if (!ale) return JS_FALSE; EMIT_ATOM_INDEX_OP(JSOP_STRING, ALE_INDEX(ale)); } if (js_Emit1(cx, cg, pn->pn_op) < 0) return JS_FALSE; } #ifdef DEBUG else JS_ASSERT(pn->pn_count != 0); #endif break; case TOK_XMLPTAGC: if (pn->pn_op == JSOP_XMLOBJECT) { ok = EmitAtomOp(cx, pn, pn->pn_op, cg); break; } /* FALL THROUGH */ case TOK_XMLSTAGO: case TOK_XMLETAGO: { uint32 i; if (js_Emit1(cx, cg, JSOP_STARTXML) < 0) return JS_FALSE; ale = js_IndexAtom(cx, (pn->pn_type == TOK_XMLETAGO) ? cx->runtime->atomState.etagoAtom : cx->runtime->atomState.stagoAtom, &cg->atomList); if (!ale) return JS_FALSE; EMIT_ATOM_INDEX_OP(JSOP_STRING, ALE_INDEX(ale)); JS_ASSERT(pn->pn_count != 0); pn2 = pn->pn_head; if (pn2->pn_type == TOK_LC && js_Emit1(cx, cg, JSOP_STARTXMLEXPR) < 0) return JS_FALSE; if (!js_EmitTree(cx, cg, pn2)) return JS_FALSE; if (js_Emit1(cx, cg, JSOP_ADD) < 0) return JS_FALSE; for (pn2 = pn2->pn_next, i = 0; pn2; pn2 = pn2->pn_next, i++) { if (pn2->pn_type == TOK_LC && js_Emit1(cx, cg, JSOP_STARTXMLEXPR) < 0) { return JS_FALSE; } if (!js_EmitTree(cx, cg, pn2)) return JS_FALSE; if ((i & 1) && pn2->pn_type == TOK_LC) { if (js_Emit1(cx, cg, JSOP_TOATTRVAL) < 0) return JS_FALSE; } if (js_Emit1(cx, cg, (i & 1) ? JSOP_ADDATTRVAL : JSOP_ADDATTRNAME) < 0) { return JS_FALSE; } } ale = js_IndexAtom(cx, (pn->pn_type == TOK_XMLPTAGC) ? cx->runtime->atomState.ptagcAtom : cx->runtime->atomState.tagcAtom, &cg->atomList); if (!ale) return JS_FALSE; EMIT_ATOM_INDEX_OP(JSOP_STRING, ALE_INDEX(ale)); if (js_Emit1(cx, cg, JSOP_ADD) < 0) return JS_FALSE; if ((pn->pn_extra & PNX_XMLROOT) && js_Emit1(cx, cg, pn->pn_op) < 0) return JS_FALSE; break; } case TOK_XMLNAME: if (pn->pn_arity == PN_LIST) { JS_ASSERT(pn->pn_count != 0); for (pn2 = pn->pn_head; pn2; pn2 = pn2->pn_next) { if (!js_EmitTree(cx, cg, pn2)) return JS_FALSE; if (pn2 != pn->pn_head && js_Emit1(cx, cg, JSOP_ADD) < 0) return JS_FALSE; } } else { JS_ASSERT(pn->pn_arity == PN_NULLARY); ok = EmitAtomOp(cx, pn, pn->pn_op, cg); } break; case TOK_XMLPI: ale = js_IndexAtom(cx, pn->pn_atom2, &cg->atomList); if (!ale) return JS_FALSE; if (!EmitAtomIndexOp(cx, JSOP_QNAMEPART, ALE_INDEX(ale), cg)) return JS_FALSE; if (!EmitAtomOp(cx, pn, JSOP_XMLPI, cg)) return JS_FALSE; break; #endif /* JS_HAS_XML_SUPPORT */ default: JS_ASSERT(0); } if (ok && --cg->emitLevel == 0 && cg->spanDeps) ok = OptimizeSpanDeps(cx, cg); return ok; } /* XXX get rid of offsetBias, it's used only by SRC_FOR and SRC_DECL */ JS_FRIEND_DATA(JSSrcNoteSpec) js_SrcNoteSpec[] = { {"null", 0, 0, 0}, {"if", 0, 0, 0}, {"if-else", 2, 0, 1}, {"while", 1, 0, 1}, {"for", 3, 1, 1}, {"continue", 0, 0, 0}, {"decl", 1, 1, 1}, {"pcdelta", 1, 0, 1}, {"assignop", 0, 0, 0}, {"cond", 1, 0, 1}, {"brace", 1, 0, 1}, {"hidden", 0, 0, 0}, {"pcbase", 1, 0, -1}, {"label", 1, 0, 0}, {"labelbrace", 1, 0, 0}, {"endbrace", 0, 0, 0}, {"break2label", 1, 0, 0}, {"cont2label", 1, 0, 0}, {"switch", 2, 0, 1}, {"funcdef", 1, 0, 0}, {"catch", 1, 0, 1}, {"extended", -1, 0, 0}, {"newline", 0, 0, 0}, {"setline", 1, 0, 0}, {"xdelta", 0, 0, 0}, }; static intN AllocSrcNote(JSContext *cx, JSCodeGenerator *cg) { intN index; JSArenaPool *pool; size_t size; index = CG_NOTE_COUNT(cg); if (((uintN)index & CG_NOTE_MASK(cg)) == 0) { pool = cg->notePool; size = SRCNOTE_SIZE(CG_NOTE_MASK(cg) + 1); if (!CG_NOTES(cg)) { /* Allocate the first note array lazily; leave noteMask alone. */ JS_ARENA_ALLOCATE_CAST(CG_NOTES(cg), jssrcnote *, pool, size); } else { /* Grow by doubling note array size; update noteMask on success. */ JS_ARENA_GROW_CAST(CG_NOTES(cg), jssrcnote *, pool, size, size); if (CG_NOTES(cg)) CG_NOTE_MASK(cg) = (CG_NOTE_MASK(cg) << 1) | 1; } if (!CG_NOTES(cg)) { JS_ReportOutOfMemory(cx); return -1; } } CG_NOTE_COUNT(cg) = index + 1; return index; } intN js_NewSrcNote(JSContext *cx, JSCodeGenerator *cg, JSSrcNoteType type) { intN index, n; jssrcnote *sn; ptrdiff_t offset, delta, xdelta; /* * Claim a note slot in CG_NOTES(cg) by growing it if necessary and then * incrementing CG_NOTE_COUNT(cg). */ index = AllocSrcNote(cx, cg); if (index < 0) return -1; sn = &CG_NOTES(cg)[index]; /* * Compute delta from the last annotated bytecode's offset. If it's too * big to fit in sn, allocate one or more xdelta notes and reset sn. */ offset = CG_OFFSET(cg); delta = offset - CG_LAST_NOTE_OFFSET(cg); CG_LAST_NOTE_OFFSET(cg) = offset; if (delta >= SN_DELTA_LIMIT) { do { xdelta = JS_MIN(delta, SN_XDELTA_MASK); SN_MAKE_XDELTA(sn, xdelta); delta -= xdelta; index = AllocSrcNote(cx, cg); if (index < 0) return -1; sn = &CG_NOTES(cg)[index]; } while (delta >= SN_DELTA_LIMIT); } /* * Initialize type and delta, then allocate the minimum number of notes * needed for type's arity. Usually, we won't need more, but if an offset * does take two bytes, js_SetSrcNoteOffset will grow CG_NOTES(cg). */ SN_MAKE_NOTE(sn, type, delta); for (n = (intN)js_SrcNoteSpec[type].arity; n > 0; n--) { if (js_NewSrcNote(cx, cg, SRC_NULL) < 0) return -1; } return index; } intN js_NewSrcNote2(JSContext *cx, JSCodeGenerator *cg, JSSrcNoteType type, ptrdiff_t offset) { intN index; index = js_NewSrcNote(cx, cg, type); if (index >= 0) { if (!js_SetSrcNoteOffset(cx, cg, index, 0, offset)) return -1; } return index; } intN js_NewSrcNote3(JSContext *cx, JSCodeGenerator *cg, JSSrcNoteType type, ptrdiff_t offset1, ptrdiff_t offset2) { intN index; index = js_NewSrcNote(cx, cg, type); if (index >= 0) { if (!js_SetSrcNoteOffset(cx, cg, index, 0, offset1)) return -1; if (!js_SetSrcNoteOffset(cx, cg, index, 1, offset2)) return -1; } return index; } static JSBool GrowSrcNotes(JSContext *cx, JSCodeGenerator *cg) { JSArenaPool *pool; size_t size; /* Grow by doubling note array size; update noteMask on success. */ pool = cg->notePool; size = SRCNOTE_SIZE(CG_NOTE_MASK(cg) + 1); JS_ARENA_GROW_CAST(CG_NOTES(cg), jssrcnote *, pool, size, size); if (!CG_NOTES(cg)) { JS_ReportOutOfMemory(cx); return JS_FALSE; } CG_NOTE_MASK(cg) = (CG_NOTE_MASK(cg) << 1) | 1; return JS_TRUE; } jssrcnote * js_AddToSrcNoteDelta(JSContext *cx, JSCodeGenerator *cg, jssrcnote *sn, ptrdiff_t delta) { ptrdiff_t base, limit, newdelta, diff; intN index; /* * Called only from OptimizeSpanDeps and js_FinishTakingSrcNotes to add to * main script note deltas, and only by a small positive amount. */ JS_ASSERT(cg->current == &cg->main); JS_ASSERT((unsigned) delta < (unsigned) SN_XDELTA_LIMIT); base = SN_DELTA(sn); limit = SN_IS_XDELTA(sn) ? SN_XDELTA_LIMIT : SN_DELTA_LIMIT; newdelta = base + delta; if (newdelta < limit) { SN_SET_DELTA(sn, newdelta); } else { index = sn - cg->main.notes; if ((cg->main.noteCount & cg->main.noteMask) == 0) { if (!GrowSrcNotes(cx, cg)) return NULL; sn = cg->main.notes + index; } diff = cg->main.noteCount - index; cg->main.noteCount++; memmove(sn + 1, sn, SRCNOTE_SIZE(diff)); SN_MAKE_XDELTA(sn, delta); sn++; } return sn; } JS_FRIEND_API(uintN) js_SrcNoteLength(jssrcnote *sn) { uintN arity; jssrcnote *base; arity = (intN)js_SrcNoteSpec[SN_TYPE(sn)].arity; for (base = sn++; arity; sn++, arity--) { if (*sn & SN_3BYTE_OFFSET_FLAG) sn += 2; } return sn - base; } JS_FRIEND_API(ptrdiff_t) js_GetSrcNoteOffset(jssrcnote *sn, uintN which) { /* Find the offset numbered which (i.e., skip exactly which offsets). */ JS_ASSERT(SN_TYPE(sn) != SRC_XDELTA); JS_ASSERT(which < js_SrcNoteSpec[SN_TYPE(sn)].arity); for (sn++; which; sn++, which--) { if (*sn & SN_3BYTE_OFFSET_FLAG) sn += 2; } if (*sn & SN_3BYTE_OFFSET_FLAG) { return (ptrdiff_t)(((uint32)(sn[0] & SN_3BYTE_OFFSET_MASK) << 16) | (sn[1] << 8) | sn[2]); } return (ptrdiff_t)*sn; } JSBool js_SetSrcNoteOffset(JSContext *cx, JSCodeGenerator *cg, uintN index, uintN which, ptrdiff_t offset) { jssrcnote *sn; ptrdiff_t diff; if ((jsuword)offset >= (jsuword)((ptrdiff_t)SN_3BYTE_OFFSET_FLAG << 16)) { ReportStatementTooLarge(cx, cg); return JS_FALSE; } /* Find the offset numbered which (i.e., skip exactly which offsets). */ sn = &CG_NOTES(cg)[index]; JS_ASSERT(SN_TYPE(sn) != SRC_XDELTA); JS_ASSERT(which < js_SrcNoteSpec[SN_TYPE(sn)].arity); for (sn++; which; sn++, which--) { if (*sn & SN_3BYTE_OFFSET_FLAG) sn += 2; } /* See if the new offset requires three bytes. */ if (offset > (ptrdiff_t)SN_3BYTE_OFFSET_MASK) { /* Maybe this offset was already set to a three-byte value. */ if (!(*sn & SN_3BYTE_OFFSET_FLAG)) { /* Losing, need to insert another two bytes for this offset. */ index = PTRDIFF(sn, CG_NOTES(cg), jssrcnote); /* * Simultaneously test to see if the source note array must grow to * accomodate either the first or second byte of additional storage * required by this 3-byte offset. */ if (((CG_NOTE_COUNT(cg) + 1) & CG_NOTE_MASK(cg)) <= 1) { if (!GrowSrcNotes(cx, cg)) return JS_FALSE; sn = CG_NOTES(cg) + index; } CG_NOTE_COUNT(cg) += 2; diff = CG_NOTE_COUNT(cg) - (index + 3); JS_ASSERT(diff >= 0); if (diff > 0) memmove(sn + 3, sn + 1, SRCNOTE_SIZE(diff)); } *sn++ = (jssrcnote)(SN_3BYTE_OFFSET_FLAG | (offset >> 16)); *sn++ = (jssrcnote)(offset >> 8); } *sn = (jssrcnote)offset; return JS_TRUE; } #ifdef DEBUG_notme #define DEBUG_srcnotesize #endif #ifdef DEBUG_srcnotesize #define NBINS 10 static uint32 hist[NBINS]; void DumpSrcNoteSizeHist() { static FILE *fp; int i, n; if (!fp) { fp = fopen("/tmp/srcnotes.hist", "w"); if (!fp) return; setvbuf(fp, NULL, _IONBF, 0); } fprintf(fp, "SrcNote size histogram:\n"); for (i = 0; i < NBINS; i++) { fprintf(fp, "%4u %4u ", JS_BIT(i), hist[i]); for (n = (int) JS_HOWMANY(hist[i], 10); n > 0; --n) fputc('*', fp); fputc('\n', fp); } fputc('\n', fp); } #endif /* * Fill in the storage at notes with prolog and main srcnotes; the space at * notes was allocated using the CG_COUNT_FINAL_SRCNOTES macro from jsemit.h. * SO DON'T CHANGE THIS FUNCTION WITHOUT AT LEAST CHECKING WHETHER jsemit.h's * CG_COUNT_FINAL_SRCNOTES MACRO NEEDS CORRESPONDING CHANGES! */ JSBool js_FinishTakingSrcNotes(JSContext *cx, JSCodeGenerator *cg, jssrcnote *notes) { uintN prologCount, mainCount, totalCount; ptrdiff_t offset, delta; jssrcnote *sn; JS_ASSERT(cg->current == &cg->main); prologCount = cg->prolog.noteCount; if (prologCount && cg->prolog.currentLine != cg->firstLine) { CG_SWITCH_TO_PROLOG(cg); if (js_NewSrcNote2(cx, cg, SRC_SETLINE, (ptrdiff_t)cg->firstLine) < 0) return JS_FALSE; prologCount = cg->prolog.noteCount; CG_SWITCH_TO_MAIN(cg); } else { /* * Either no prolog srcnotes, or no line number change over prolog. * We don't need a SRC_SETLINE, but we may need to adjust the offset * of the first main note, by adding to its delta and possibly even * prepending SRC_XDELTA notes to it to account for prolog bytecodes * that came at and after the last annotated bytecode. */ offset = CG_PROLOG_OFFSET(cg) - cg->prolog.lastNoteOffset; JS_ASSERT(offset >= 0); if (offset > 0 && cg->main.noteCount != 0) { /* NB: Use as much of the first main note's delta as we can. */ sn = cg->main.notes; delta = SN_IS_XDELTA(sn) ? SN_XDELTA_MASK - (*sn & SN_XDELTA_MASK) : SN_DELTA_MASK - (*sn & SN_DELTA_MASK); if (offset < delta) delta = offset; for (;;) { if (!js_AddToSrcNoteDelta(cx, cg, sn, delta)) return JS_FALSE; offset -= delta; if (offset == 0) break; delta = JS_MIN(offset, SN_XDELTA_MASK); sn = cg->main.notes; } } } mainCount = cg->main.noteCount; totalCount = prologCount + mainCount; if (prologCount) memcpy(notes, cg->prolog.notes, SRCNOTE_SIZE(prologCount)); memcpy(notes + prologCount, cg->main.notes, SRCNOTE_SIZE(mainCount)); SN_MAKE_TERMINATOR(¬es[totalCount]); #ifdef DEBUG_notme { int bin = JS_CeilingLog2(totalCount); if (bin >= NBINS) bin = NBINS - 1; ++hist[bin]; } #endif return JS_TRUE; } JSBool js_AllocTryNotes(JSContext *cx, JSCodeGenerator *cg) { size_t size, incr; ptrdiff_t delta; size = TRYNOTE_SIZE(cg->treeContext.tryCount); if (size <= cg->tryNoteSpace) return JS_TRUE; /* * Allocate trynotes from cx->tempPool. * XXX Too much growing and we bloat, as other tempPool allocators block * in-place growth, and we never recycle old free space in an arena. * YYY But once we consume an entire arena, we'll realloc it, letting the * malloc heap recycle old space, while still freeing _en masse_ via the * arena pool. */ if (!cg->tryBase) { size = JS_ROUNDUP(size, TRYNOTE_SIZE(TRYNOTE_CHUNK)); JS_ARENA_ALLOCATE_CAST(cg->tryBase, JSTryNote *, &cx->tempPool, size); if (!cg->tryBase) return JS_FALSE; cg->tryNoteSpace = size; cg->tryNext = cg->tryBase; } else { delta = PTRDIFF((char *)cg->tryNext, (char *)cg->tryBase, char); incr = size - cg->tryNoteSpace; incr = JS_ROUNDUP(incr, TRYNOTE_SIZE(TRYNOTE_CHUNK)); size = cg->tryNoteSpace; JS_ARENA_GROW_CAST(cg->tryBase, JSTryNote *, &cx->tempPool, size, incr); if (!cg->tryBase) return JS_FALSE; cg->tryNoteSpace = size + incr; cg->tryNext = (JSTryNote *)((char *)cg->tryBase + delta); } return JS_TRUE; } JSTryNote * js_NewTryNote(JSContext *cx, JSCodeGenerator *cg, ptrdiff_t start, ptrdiff_t end, ptrdiff_t catchStart) { JSTryNote *tn; JS_ASSERT(cg->tryBase <= cg->tryNext); JS_ASSERT(catchStart >= 0); tn = cg->tryNext++; tn->start = start; tn->length = end - start; tn->catchStart = catchStart; return tn; } void js_FinishTakingTryNotes(JSContext *cx, JSCodeGenerator *cg, JSTryNote *notes) { uintN count; count = PTRDIFF(cg->tryNext, cg->tryBase, JSTryNote); if (!count) return; memcpy(notes, cg->tryBase, TRYNOTE_SIZE(count)); notes[count].start = 0; notes[count].length = CG_OFFSET(cg); notes[count].catchStart = 0; } pacparser-1.4.5/src/spidermonkey/js/src/jsemit.h000066400000000000000000001050501464010763600216600ustar00rootroot00000000000000/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- * vim: set ts=8 sw=4 et tw=78: * * ***** BEGIN LICENSE BLOCK ***** * Version: MPL 1.1/GPL 2.0/LGPL 2.1 * * The contents of this file are subject to the Mozilla Public License Version * 1.1 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * http://www.mozilla.org/MPL/ * * Software distributed under the License is distributed on an "AS IS" basis, * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License * for the specific language governing rights and limitations under the * License. * * The Original Code is Mozilla Communicator client code, released * March 31, 1998. * * The Initial Developer of the Original Code is * Netscape Communications Corporation. * Portions created by the Initial Developer are Copyright (C) 1998 * the Initial Developer. All Rights Reserved. * * Contributor(s): * * Alternatively, the contents of this file may be used under the terms of * either of the GNU General Public License Version 2 or later (the "GPL"), * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), * in which case the provisions of the GPL or the LGPL are applicable instead * of those above. If you wish to allow use of your version of this file only * under the terms of either the GPL or the LGPL, and not to allow others to * use your version of this file under the terms of the MPL, indicate your * decision by deleting the provisions above and replace them with the notice * and other provisions required by the GPL or the LGPL. If you do not delete * the provisions above, a recipient may use your version of this file under * the terms of any one of the MPL, the GPL or the LGPL. * * ***** END LICENSE BLOCK ***** */ #ifndef jsemit_h___ #define jsemit_h___ /* * JS bytecode generation. */ #include "jsstddef.h" #include "jstypes.h" #include "jsatom.h" #include "jsopcode.h" #include "jsprvtd.h" #include "jspubtd.h" JS_BEGIN_EXTERN_C /* * NB: If you add enumerators for scope statements, add them between STMT_WITH * and STMT_CATCH, or you will break the STMT_TYPE_IS_SCOPE macro. If you add * non-looping statement enumerators, add them before STMT_DO_LOOP or you will * break the STMT_TYPE_IS_LOOP macro. * * Also remember to keep the statementName array in jsemit.c in sync. */ typedef enum JSStmtType { STMT_LABEL, /* labeled statement: L: s */ STMT_IF, /* if (then) statement */ STMT_ELSE, /* else clause of if statement */ STMT_BODY, /* synthetic body of function with destructuring formal parameters */ STMT_BLOCK, /* compound statement: { s1[;... sN] } */ STMT_SWITCH, /* switch statement */ STMT_WITH, /* with statement */ STMT_CATCH, /* catch block */ STMT_TRY, /* try block */ STMT_FINALLY, /* finally block */ STMT_SUBROUTINE, /* gosub-target subroutine body */ STMT_DO_LOOP, /* do/while loop statement */ STMT_FOR_LOOP, /* for loop statement */ STMT_FOR_IN_LOOP, /* for/in loop statement */ STMT_WHILE_LOOP /* while loop statement */ } JSStmtType; #define STMT_TYPE_IN_RANGE(t,b,e) ((uint)((t) - (b)) <= (uintN)((e) - (b))) /* * A comment on the encoding of the JSStmtType enum and type-testing macros: * * STMT_TYPE_MAYBE_SCOPE tells whether a statement type is always, or may * become, a lexical scope. It therefore includes block and switch (the two * low-numbered "maybe" scope types) and excludes with (with has dynamic scope * pending the "reformed with" in ES4/JS2). It includes all try-catch-finally * types, which are high-numbered maybe-scope types. * * STMT_TYPE_LINKS_SCOPE tells whether a JSStmtInfo of the given type eagerly * links to other scoping statement info records. It excludes the two early * "maybe" types, block and switch, as well as the try and both finally types, * since try and the other trailing maybe-scope types don't need block scope * unless they contain let declarations. * * We treat with as a static scope because it prevents lexical binding from * continuing further up the static scope chain. With the "reformed with" * proposal for JS2, we'll be able to model it statically, too. */ #define STMT_TYPE_MAYBE_SCOPE(type) \ (type != STMT_WITH && \ STMT_TYPE_IN_RANGE(type, STMT_BLOCK, STMT_SUBROUTINE)) #define STMT_TYPE_LINKS_SCOPE(type) \ STMT_TYPE_IN_RANGE(type, STMT_WITH, STMT_CATCH) #define STMT_TYPE_IS_TRYING(type) \ STMT_TYPE_IN_RANGE(type, STMT_TRY, STMT_SUBROUTINE) #define STMT_TYPE_IS_LOOP(type) ((type) >= STMT_DO_LOOP) #define STMT_MAYBE_SCOPE(stmt) STMT_TYPE_MAYBE_SCOPE((stmt)->type) #define STMT_LINKS_SCOPE(stmt) (STMT_TYPE_LINKS_SCOPE((stmt)->type) || \ ((stmt)->flags & SIF_SCOPE)) #define STMT_IS_TRYING(stmt) STMT_TYPE_IS_TRYING((stmt)->type) #define STMT_IS_LOOP(stmt) STMT_TYPE_IS_LOOP((stmt)->type) typedef struct JSStmtInfo JSStmtInfo; struct JSStmtInfo { uint16 type; /* statement type */ uint16 flags; /* flags, see below */ ptrdiff_t update; /* loop update offset (top if none) */ ptrdiff_t breaks; /* offset of last break in loop */ ptrdiff_t continues; /* offset of last continue in loop */ JSAtom *atom; /* name of LABEL, or block scope object */ JSStmtInfo *down; /* info for enclosing statement */ JSStmtInfo *downScope; /* next enclosing lexical scope */ }; #define SIF_SCOPE 0x0001 /* statement has its own lexical scope */ #define SIF_BODY_BLOCK 0x0002 /* STMT_BLOCK type is a function body */ /* * To reuse space in JSStmtInfo, rename breaks and continues for use during * try/catch/finally code generation and backpatching. To match most common * use cases, the macro argument is a struct, not a struct pointer. Only a * loop, switch, or label statement info record can have breaks and continues, * and only a for loop has an update backpatch chain, so it's safe to overlay * these for the "trying" JSStmtTypes. */ #define CATCHNOTE(stmt) ((stmt).update) #define GOSUBS(stmt) ((stmt).breaks) #define GUARDJUMP(stmt) ((stmt).continues) #define AT_TOP_LEVEL(tc) \ (!(tc)->topStmt || ((tc)->topStmt->flags & SIF_BODY_BLOCK)) #define SET_STATEMENT_TOP(stmt, top) \ ((stmt)->update = (top), (stmt)->breaks = (stmt)->continues = (-1)) struct JSTreeContext { /* tree context for semantic checks */ uint16 flags; /* statement state flags, see below */ uint16 numGlobalVars; /* max. no. of global variables/regexps */ uint32 tryCount; /* total count of try statements parsed */ uint32 globalUses; /* optimizable global var uses in total */ uint32 loopyGlobalUses;/* optimizable global var uses in loops */ JSStmtInfo *topStmt; /* top of statement info stack */ JSStmtInfo *topScopeStmt; /* top lexical scope statement */ JSObject *blockChain; /* compile time block scope chain (NB: one deeper than the topScopeStmt/downScope chain when in head of let block/expr) */ JSParseNode *blockNode; /* parse node for a lexical scope. XXX combine with blockChain? */ JSAtomList decls; /* function, const, and var declarations */ JSParseNode *nodeList; /* list of recyclable parse-node structs */ }; #define TCF_COMPILING 0x01 /* generating bytecode; this tc is a cg */ #define TCF_IN_FUNCTION 0x02 /* parsing inside function body */ #define TCF_RETURN_EXPR 0x04 /* function has 'return expr;' */ #define TCF_RETURN_VOID 0x08 /* function has 'return;' */ #define TCF_RETURN_FLAGS 0x0C /* propagate these out of blocks */ #define TCF_IN_FOR_INIT 0x10 /* parsing init expr of for; exclude 'in' */ #define TCF_FUN_CLOSURE_VS_VAR 0x20 /* function and var with same name */ #define TCF_FUN_USES_NONLOCALS 0x40 /* function refers to non-local names */ #define TCF_FUN_HEAVYWEIGHT 0x80 /* function needs Call object per call */ #define TCF_FUN_IS_GENERATOR 0x100 /* parsed yield statement in function */ #define TCF_FUN_FLAGS 0x1E0 /* flags to propagate from FunctionBody */ #define TCF_HAS_DEFXMLNS 0x200 /* default xml namespace = ...; parsed */ #define TCF_HAS_FUNCTION_STMT 0x400 /* block contains a function statement */ #define TREE_CONTEXT_INIT(tc) \ ((tc)->flags = (tc)->numGlobalVars = 0, \ (tc)->tryCount = (tc)->globalUses = (tc)->loopyGlobalUses = 0, \ (tc)->topStmt = (tc)->topScopeStmt = NULL, \ (tc)->blockChain = NULL, \ ATOM_LIST_INIT(&(tc)->decls), \ (tc)->nodeList = NULL, (tc)->blockNode = NULL) #define TREE_CONTEXT_FINISH(tc) \ ((void)0) /* * Span-dependent instructions are jumps whose span (from the jump bytecode to * the jump target) may require 2 or 4 bytes of immediate operand. */ typedef struct JSSpanDep JSSpanDep; typedef struct JSJumpTarget JSJumpTarget; struct JSSpanDep { ptrdiff_t top; /* offset of first bytecode in an opcode */ ptrdiff_t offset; /* offset - 1 within opcode of jump operand */ ptrdiff_t before; /* original offset - 1 of jump operand */ JSJumpTarget *target; /* tagged target pointer or backpatch delta */ }; /* * Jump targets are stored in an AVL tree, for O(log(n)) lookup with targets * sorted by offset from left to right, so that targets after a span-dependent * instruction whose jump offset operand must be extended can be found quickly * and adjusted upward (toward higher offsets). */ struct JSJumpTarget { ptrdiff_t offset; /* offset of span-dependent jump target */ int balance; /* AVL tree balance number */ JSJumpTarget *kids[2]; /* left and right AVL tree child pointers */ }; #define JT_LEFT 0 #define JT_RIGHT 1 #define JT_OTHER_DIR(dir) (1 - (dir)) #define JT_IMBALANCE(dir) (((dir) << 1) - 1) #define JT_DIR(imbalance) (((imbalance) + 1) >> 1) /* * Backpatch deltas are encoded in JSSpanDep.target if JT_TAG_BIT is clear, * so we can maintain backpatch chains when using span dependency records to * hold jump offsets that overflow 16 bits. */ #define JT_TAG_BIT ((jsword) 1) #define JT_UNTAG_SHIFT 1 #define JT_SET_TAG(jt) ((JSJumpTarget *)((jsword)(jt) | JT_TAG_BIT)) #define JT_CLR_TAG(jt) ((JSJumpTarget *)((jsword)(jt) & ~JT_TAG_BIT)) #define JT_HAS_TAG(jt) ((jsword)(jt) & JT_TAG_BIT) #define BITS_PER_PTRDIFF (sizeof(ptrdiff_t) * JS_BITS_PER_BYTE) #define BITS_PER_BPDELTA (BITS_PER_PTRDIFF - 1 - JT_UNTAG_SHIFT) #define BPDELTA_MAX (((ptrdiff_t)1 << BITS_PER_BPDELTA) - 1) #define BPDELTA_TO_JT(bp) ((JSJumpTarget *)((bp) << JT_UNTAG_SHIFT)) #define JT_TO_BPDELTA(jt) ((ptrdiff_t)((jsword)(jt) >> JT_UNTAG_SHIFT)) #define SD_SET_TARGET(sd,jt) ((sd)->target = JT_SET_TAG(jt)) #define SD_GET_TARGET(sd) (JS_ASSERT(JT_HAS_TAG((sd)->target)), \ JT_CLR_TAG((sd)->target)) #define SD_SET_BPDELTA(sd,bp) ((sd)->target = BPDELTA_TO_JT(bp)) #define SD_GET_BPDELTA(sd) (JS_ASSERT(!JT_HAS_TAG((sd)->target)), \ JT_TO_BPDELTA((sd)->target)) /* Avoid asserting twice by expanding SD_GET_TARGET in the "then" clause. */ #define SD_SPAN(sd,pivot) (SD_GET_TARGET(sd) \ ? JT_CLR_TAG((sd)->target)->offset - (pivot) \ : 0) struct JSCodeGenerator { JSTreeContext treeContext; /* base state: statement info stack, etc. */ JSArenaPool *codePool; /* pointer to thread code arena pool */ JSArenaPool *notePool; /* pointer to thread srcnote arena pool */ void *codeMark; /* low watermark in cg->codePool */ void *noteMark; /* low watermark in cg->notePool */ void *tempMark; /* low watermark in cx->tempPool */ struct { jsbytecode *base; /* base of JS bytecode vector */ jsbytecode *limit; /* one byte beyond end of bytecode */ jsbytecode *next; /* pointer to next free bytecode */ jssrcnote *notes; /* source notes, see below */ uintN noteCount; /* number of source notes so far */ uintN noteMask; /* growth increment for notes */ ptrdiff_t lastNoteOffset; /* code offset for last source note */ uintN currentLine; /* line number for tree-based srcnote gen */ } prolog, main, *current; const char *filename; /* null or weak link to source filename */ uintN firstLine; /* first line, for js_NewScriptFromCG */ JSPrincipals *principals; /* principals for constant folding eval */ JSAtomList atomList; /* literals indexed for mapping */ intN stackDepth; /* current stack depth in script frame */ uintN maxStackDepth; /* maximum stack depth so far */ JSTryNote *tryBase; /* first exception handling note */ JSTryNote *tryNext; /* next available note */ size_t tryNoteSpace; /* # of bytes allocated at tryBase */ JSSpanDep *spanDeps; /* span dependent instruction records */ JSJumpTarget *jumpTargets; /* AVL tree of jump target offsets */ JSJumpTarget *jtFreeList; /* JT_LEFT-linked list of free structs */ uintN numSpanDeps; /* number of span dependencies */ uintN numJumpTargets; /* number of jump targets */ ptrdiff_t spanDepTodo; /* offset from main.base of potentially unoptimized spandeps */ uintN arrayCompSlot; /* stack slot of array in comprehension */ uintN emitLevel; /* js_EmitTree recursion level */ JSAtomList constList; /* compile time constants */ JSCodeGenerator *parent; /* Enclosing function or global context */ }; #define CG_BASE(cg) ((cg)->current->base) #define CG_LIMIT(cg) ((cg)->current->limit) #define CG_NEXT(cg) ((cg)->current->next) #define CG_CODE(cg,offset) (CG_BASE(cg) + (offset)) #define CG_OFFSET(cg) PTRDIFF(CG_NEXT(cg), CG_BASE(cg), jsbytecode) #define CG_NOTES(cg) ((cg)->current->notes) #define CG_NOTE_COUNT(cg) ((cg)->current->noteCount) #define CG_NOTE_MASK(cg) ((cg)->current->noteMask) #define CG_LAST_NOTE_OFFSET(cg) ((cg)->current->lastNoteOffset) #define CG_CURRENT_LINE(cg) ((cg)->current->currentLine) #define CG_PROLOG_BASE(cg) ((cg)->prolog.base) #define CG_PROLOG_LIMIT(cg) ((cg)->prolog.limit) #define CG_PROLOG_NEXT(cg) ((cg)->prolog.next) #define CG_PROLOG_CODE(cg,poff) (CG_PROLOG_BASE(cg) + (poff)) #define CG_PROLOG_OFFSET(cg) PTRDIFF(CG_PROLOG_NEXT(cg), CG_PROLOG_BASE(cg),\ jsbytecode) #define CG_SWITCH_TO_MAIN(cg) ((cg)->current = &(cg)->main) #define CG_SWITCH_TO_PROLOG(cg) ((cg)->current = &(cg)->prolog) /* * Initialize cg to allocate bytecode space from codePool, source note space * from notePool, and all other arena-allocated temporaries from cx->tempPool. * Return true on success. Report an error and return false if the initial * code segment can't be allocated. */ extern JS_FRIEND_API(JSBool) js_InitCodeGenerator(JSContext *cx, JSCodeGenerator *cg, JSArenaPool *codePool, JSArenaPool *notePool, const char *filename, uintN lineno, JSPrincipals *principals); /* * Release cg->codePool, cg->notePool, and cx->tempPool to marks set by * js_InitCodeGenerator. Note that cgs are magic: they own the arena pool * "tops-of-stack" space above their codeMark, noteMark, and tempMark points. * This means you cannot alloc from tempPool and save the pointer beyond the * next JS_FinishCodeGenerator. */ extern JS_FRIEND_API(void) js_FinishCodeGenerator(JSContext *cx, JSCodeGenerator *cg); /* * Emit one bytecode. */ extern ptrdiff_t js_Emit1(JSContext *cx, JSCodeGenerator *cg, JSOp op); /* * Emit two bytecodes, an opcode (op) with a byte of immediate operand (op1). */ extern ptrdiff_t js_Emit2(JSContext *cx, JSCodeGenerator *cg, JSOp op, jsbytecode op1); /* * Emit three bytecodes, an opcode with two bytes of immediate operands. */ extern ptrdiff_t js_Emit3(JSContext *cx, JSCodeGenerator *cg, JSOp op, jsbytecode op1, jsbytecode op2); /* * Emit (1 + extra) bytecodes, for N bytes of op and its immediate operand. */ extern ptrdiff_t js_EmitN(JSContext *cx, JSCodeGenerator *cg, JSOp op, size_t extra); /* * Unsafe macro to call js_SetJumpOffset and return false if it does. */ #define CHECK_AND_SET_JUMP_OFFSET(cx,cg,pc,off) \ JS_BEGIN_MACRO \ if (!js_SetJumpOffset(cx, cg, pc, off)) \ return JS_FALSE; \ JS_END_MACRO #define CHECK_AND_SET_JUMP_OFFSET_AT(cx,cg,off) \ CHECK_AND_SET_JUMP_OFFSET(cx, cg, CG_CODE(cg,off), CG_OFFSET(cg) - (off)) extern JSBool js_SetJumpOffset(JSContext *cx, JSCodeGenerator *cg, jsbytecode *pc, ptrdiff_t off); /* Test whether we're in a statement of given type. */ extern JSBool js_InStatement(JSTreeContext *tc, JSStmtType type); /* Test whether we're in a with statement. */ #define js_InWithStatement(tc) js_InStatement(tc, STMT_WITH) /* * Test whether atom refers to a global variable (or is a reference error). * Return true in *loopyp if any loops enclose the lexical reference, false * otherwise. */ extern JSBool js_IsGlobalReference(JSTreeContext *tc, JSAtom *atom, JSBool *loopyp); /* * Push the C-stack-allocated struct at stmt onto the stmtInfo stack. */ extern void js_PushStatement(JSTreeContext *tc, JSStmtInfo *stmt, JSStmtType type, ptrdiff_t top); /* * Push a block scope statement and link blockAtom's object-valued key into * tc->blockChain. To pop this statement info record, use js_PopStatement as * usual, or if appropriate (if generating code), js_PopStatementCG. */ extern void js_PushBlockScope(JSTreeContext *tc, JSStmtInfo *stmt, JSAtom *blockAtom, ptrdiff_t top); /* * Pop tc->topStmt. If the top JSStmtInfo struct is not stack-allocated, it * is up to the caller to free it. */ extern void js_PopStatement(JSTreeContext *tc); /* * Like js_PopStatement(&cg->treeContext), also patch breaks and continues * unless the top statement info record represents a try-catch-finally suite. * May fail if a jump offset overflows. */ extern JSBool js_PopStatementCG(JSContext *cx, JSCodeGenerator *cg); /* * Define and lookup a primitive jsval associated with the const named by atom. * js_DefineCompileTimeConstant analyzes the constant-folded initializer at pn * and saves the const's value in cg->constList, if it can be used at compile * time. It returns true unless an error occurred. * * If the initializer's value could not be saved, js_LookupCompileTimeConstant * calls will return the undefined value. js_LookupCompileTimeConstant tries * to find a const value memorized for atom, returning true with *vp set to a * value other than undefined if the constant was found, true with *vp set to * JSVAL_VOID if not found, and false on error. */ extern JSBool js_DefineCompileTimeConstant(JSContext *cx, JSCodeGenerator *cg, JSAtom *atom, JSParseNode *pn); extern JSBool js_LookupCompileTimeConstant(JSContext *cx, JSCodeGenerator *cg, JSAtom *atom, jsval *vp); /* * Find a lexically scoped variable (one declared by let, catch, or an array * comprehension) named by atom, looking in tc's compile-time scopes. * * If a WITH statement is reached along the scope stack, return its statement * info record, so callers can tell that atom is ambiguous. If slotp is not * null, then if atom is found, set *slotp to its stack slot, otherwise to -1. * This means that if slotp is not null, all the block objects on the lexical * scope chain must have had their depth slots computed by the code generator, * so the caller must be under js_EmitTree. * * In any event, directly return the statement info record in which atom was * found. Otherwise return null. */ extern JSStmtInfo * js_LexicalLookup(JSTreeContext *tc, JSAtom *atom, jsint *slotp, JSBool letdecl); /* * Emit code into cg for the tree rooted at pn. */ extern JSBool js_EmitTree(JSContext *cx, JSCodeGenerator *cg, JSParseNode *pn); /* * Emit function code into cg for the tree rooted at body. */ extern JSBool js_EmitFunctionBytecode(JSContext *cx, JSCodeGenerator *cg, JSParseNode *body); /* * Emit code into cg for the tree rooted at body, then create a persistent * script for fun from cg. */ extern JSBool js_EmitFunctionBody(JSContext *cx, JSCodeGenerator *cg, JSParseNode *body, JSFunction *fun); /* * Source notes generated along with bytecode for decompiling and debugging. * A source note is a uint8 with 5 bits of type and 3 of offset from the pc of * the previous note. If 3 bits of offset aren't enough, extended delta notes * (SRC_XDELTA) consisting of 2 set high order bits followed by 6 offset bits * are emitted before the next note. Some notes have operand offsets encoded * immediately after them, in note bytes or byte-triples. * * Source Note Extended Delta * +7-6-5-4-3+2-1-0+ +7-6-5+4-3-2-1-0+ * |note-type|delta| |1 1| ext-delta | * +---------+-----+ +---+-----------+ * * At most one "gettable" note (i.e., a note of type other than SRC_NEWLINE, * SRC_SETLINE, and SRC_XDELTA) applies to a given bytecode. * * NB: the js_SrcNoteSpec array in jsemit.c is indexed by this enum, so its * initializers need to match the order here. * * Note on adding new source notes: every pair of bytecodes (A, B) where A and * B have disjoint sets of source notes that could apply to each bytecode may * reuse the same note type value for two notes (snA, snB) that have the same * arity, offsetBias, and isSpanDep initializers in js_SrcNoteSpec. This is * why SRC_IF and SRC_INITPROP have the same value below. For bad historical * reasons, some bytecodes below that could be overlayed have not been, but * before using SRC_EXTENDED, consider compressing the existing note types. * * Don't forget to update JSXDR_BYTECODE_VERSION in jsxdrapi.h for all such * incompatible source note or other bytecode changes. */ typedef enum JSSrcNoteType { SRC_NULL = 0, /* terminates a note vector */ SRC_IF = 1, /* JSOP_IFEQ bytecode is from an if-then */ SRC_INITPROP = 1, /* disjoint meaning applied to JSOP_INITELEM or to an index label in a regular (structuring) or a destructuring object initialiser */ SRC_IF_ELSE = 2, /* JSOP_IFEQ bytecode is from an if-then-else */ SRC_WHILE = 3, /* JSOP_IFEQ is from a while loop */ SRC_FOR = 4, /* JSOP_NOP or JSOP_POP in for loop head */ SRC_CONTINUE = 5, /* JSOP_GOTO is a continue, not a break; also used on JSOP_ENDINIT if extra comma at end of array literal: [1,2,,] */ SRC_DECL = 6, /* type of a declaration (var, const, let*) */ SRC_DESTRUCT = 6, /* JSOP_DUP starting a destructuring assignment operation, with SRC_DECL_* offset operand */ SRC_PCDELTA = 7, /* distance forward from comma-operator to next POP, or from CONDSWITCH to first CASE opcode, etc. -- always a forward delta */ SRC_GROUPASSIGN = 7, /* SRC_DESTRUCT variant for [a, b] = [c, d] */ SRC_ASSIGNOP = 8, /* += or another assign-op follows */ SRC_COND = 9, /* JSOP_IFEQ is from conditional ?: operator */ SRC_BRACE = 10, /* mandatory brace, for scope or to avoid dangling else */ SRC_HIDDEN = 11, /* opcode shouldn't be decompiled */ SRC_PCBASE = 12, /* distance back from annotated getprop or setprop op to left-most obj.prop.subprop bytecode -- always a backward delta */ SRC_METHODBASE = 13, /* SRC_PCBASE variant for obj.function::foo gets and sets; disjoint from SRC_LABEL by bytecode to which it applies */ SRC_LABEL = 13, /* JSOP_NOP for label: with atomid immediate */ SRC_LABELBRACE = 14, /* JSOP_NOP for label: {...} begin brace */ SRC_ENDBRACE = 15, /* JSOP_NOP for label: {...} end brace */ SRC_BREAK2LABEL = 16, /* JSOP_GOTO for 'break label' with atomid */ SRC_CONT2LABEL = 17, /* JSOP_GOTO for 'continue label' with atomid */ SRC_SWITCH = 18, /* JSOP_*SWITCH with offset to end of switch, 2nd off to first JSOP_CASE if condswitch */ SRC_FUNCDEF = 19, /* JSOP_NOP for function f() with atomid */ SRC_CATCH = 20, /* catch block has guard */ SRC_EXTENDED = 21, /* extended source note, 32-159, in next byte */ SRC_NEWLINE = 22, /* bytecode follows a source newline */ SRC_SETLINE = 23, /* a file-absolute source line number note */ SRC_XDELTA = 24 /* 24-31 are for extended delta notes */ } JSSrcNoteType; /* * Constants for the SRC_DECL source note. Note that span-dependent bytecode * selection means that any SRC_DECL offset greater than SRC_DECL_LET may need * to be adjusted, but these "offsets" are too small to span a span-dependent * instruction, so can be used to denote distinct declaration syntaxes to the * decompiler. * * NB: the var_prefix array in jsopcode.c depends on these dense indexes from * SRC_DECL_VAR through SRC_DECL_LET. */ #define SRC_DECL_VAR 0 #define SRC_DECL_CONST 1 #define SRC_DECL_LET 2 #define SRC_DECL_NONE 3 #define SN_TYPE_BITS 5 #define SN_DELTA_BITS 3 #define SN_XDELTA_BITS 6 #define SN_TYPE_MASK (JS_BITMASK(SN_TYPE_BITS) << SN_DELTA_BITS) #define SN_DELTA_MASK ((ptrdiff_t)JS_BITMASK(SN_DELTA_BITS)) #define SN_XDELTA_MASK ((ptrdiff_t)JS_BITMASK(SN_XDELTA_BITS)) #define SN_MAKE_NOTE(sn,t,d) (*(sn) = (jssrcnote) \ (((t) << SN_DELTA_BITS) \ | ((d) & SN_DELTA_MASK))) #define SN_MAKE_XDELTA(sn,d) (*(sn) = (jssrcnote) \ ((SRC_XDELTA << SN_DELTA_BITS) \ | ((d) & SN_XDELTA_MASK))) #define SN_IS_XDELTA(sn) ((*(sn) >> SN_DELTA_BITS) >= SRC_XDELTA) #define SN_TYPE(sn) (SN_IS_XDELTA(sn) ? SRC_XDELTA \ : *(sn) >> SN_DELTA_BITS) #define SN_SET_TYPE(sn,type) SN_MAKE_NOTE(sn, type, SN_DELTA(sn)) #define SN_IS_GETTABLE(sn) (SN_TYPE(sn) < SRC_NEWLINE) #define SN_DELTA(sn) ((ptrdiff_t)(SN_IS_XDELTA(sn) \ ? *(sn) & SN_XDELTA_MASK \ : *(sn) & SN_DELTA_MASK)) #define SN_SET_DELTA(sn,delta) (SN_IS_XDELTA(sn) \ ? SN_MAKE_XDELTA(sn, delta) \ : SN_MAKE_NOTE(sn, SN_TYPE(sn), delta)) #define SN_DELTA_LIMIT ((ptrdiff_t)JS_BIT(SN_DELTA_BITS)) #define SN_XDELTA_LIMIT ((ptrdiff_t)JS_BIT(SN_XDELTA_BITS)) /* * Offset fields follow certain notes and are frequency-encoded: an offset in * [0,0x7f] consumes one byte, an offset in [0x80,0x7fffff] takes three, and * the high bit of the first byte is set. */ #define SN_3BYTE_OFFSET_FLAG 0x80 #define SN_3BYTE_OFFSET_MASK 0x7f typedef struct JSSrcNoteSpec { const char *name; /* name for disassembly/debugging output */ uint8 arity; /* number of offset operands */ uint8 offsetBias; /* bias of offset(s) from annotated pc */ int8 isSpanDep; /* 1 or -1 if offsets could span extended ops, 0 otherwise; sign tells span direction */ } JSSrcNoteSpec; extern JS_FRIEND_DATA(JSSrcNoteSpec) js_SrcNoteSpec[]; extern JS_FRIEND_API(uintN) js_SrcNoteLength(jssrcnote *sn); #define SN_LENGTH(sn) ((js_SrcNoteSpec[SN_TYPE(sn)].arity == 0) ? 1 \ : js_SrcNoteLength(sn)) #define SN_NEXT(sn) ((sn) + SN_LENGTH(sn)) /* A source note array is terminated by an all-zero element. */ #define SN_MAKE_TERMINATOR(sn) (*(sn) = SRC_NULL) #define SN_IS_TERMINATOR(sn) (*(sn) == SRC_NULL) /* * Append a new source note of the given type (and therefore size) to cg's * notes dynamic array, updating cg->noteCount. Return the new note's index * within the array pointed at by cg->current->notes. Return -1 if out of * memory. */ extern intN js_NewSrcNote(JSContext *cx, JSCodeGenerator *cg, JSSrcNoteType type); extern intN js_NewSrcNote2(JSContext *cx, JSCodeGenerator *cg, JSSrcNoteType type, ptrdiff_t offset); extern intN js_NewSrcNote3(JSContext *cx, JSCodeGenerator *cg, JSSrcNoteType type, ptrdiff_t offset1, ptrdiff_t offset2); /* * NB: this function can add at most one extra extended delta note. */ extern jssrcnote * js_AddToSrcNoteDelta(JSContext *cx, JSCodeGenerator *cg, jssrcnote *sn, ptrdiff_t delta); /* * Get and set the offset operand identified by which (0 for the first, etc.). */ extern JS_FRIEND_API(ptrdiff_t) js_GetSrcNoteOffset(jssrcnote *sn, uintN which); extern JSBool js_SetSrcNoteOffset(JSContext *cx, JSCodeGenerator *cg, uintN index, uintN which, ptrdiff_t offset); /* * Finish taking source notes in cx's notePool, copying final notes to the new * stable store allocated by the caller and passed in via notes. Return false * on malloc failure, which means this function reported an error. * * To compute the number of jssrcnotes to allocate and pass in via notes, use * the CG_COUNT_FINAL_SRCNOTES macro. This macro knows a lot about details of * js_FinishTakingSrcNotes, SO DON'T CHANGE jsemit.c's js_FinishTakingSrcNotes * FUNCTION WITHOUT CHECKING WHETHER THIS MACRO NEEDS CORRESPONDING CHANGES! */ #define CG_COUNT_FINAL_SRCNOTES(cg, cnt) \ JS_BEGIN_MACRO \ ptrdiff_t diff_ = CG_PROLOG_OFFSET(cg) - (cg)->prolog.lastNoteOffset; \ cnt = (cg)->prolog.noteCount + (cg)->main.noteCount + 1; \ if ((cg)->prolog.noteCount && \ (cg)->prolog.currentLine != (cg)->firstLine) { \ if (diff_ > SN_DELTA_MASK) \ cnt += JS_HOWMANY(diff_ - SN_DELTA_MASK, SN_XDELTA_MASK); \ cnt += 2 + (((cg)->firstLine > SN_3BYTE_OFFSET_MASK) << 1); \ } else if (diff_ > 0) { \ if (cg->main.noteCount) { \ jssrcnote *sn_ = (cg)->main.notes; \ diff_ -= SN_IS_XDELTA(sn_) \ ? SN_XDELTA_MASK - (*sn_ & SN_XDELTA_MASK) \ : SN_DELTA_MASK - (*sn_ & SN_DELTA_MASK); \ } \ if (diff_ > 0) \ cnt += JS_HOWMANY(diff_, SN_XDELTA_MASK); \ } \ JS_END_MACRO extern JSBool js_FinishTakingSrcNotes(JSContext *cx, JSCodeGenerator *cg, jssrcnote *notes); /* * Allocate cg->treeContext.tryCount notes (plus one for the end sentinel) * from cx->tempPool and set up cg->tryBase/tryNext for exactly tryCount * js_NewTryNote calls. The storage is freed by js_FinishCodeGenerator. */ extern JSBool js_AllocTryNotes(JSContext *cx, JSCodeGenerator *cg); /* * Grab the next trynote slot in cg, filling it in appropriately. */ extern JSTryNote * js_NewTryNote(JSContext *cx, JSCodeGenerator *cg, ptrdiff_t start, ptrdiff_t end, ptrdiff_t catchStart); /* * Finish generating exception information into the space at notes. As with * js_FinishTakingSrcNotes, the caller must use CG_COUNT_FINAL_TRYNOTES(cg) to * preallocate enough space in a JSTryNote[] to pass as the notes parameter of * js_FinishTakingTryNotes. */ #define CG_COUNT_FINAL_TRYNOTES(cg, cnt) \ JS_BEGIN_MACRO \ cnt = ((cg)->tryNext > (cg)->tryBase) \ ? PTRDIFF(cg->tryNext, cg->tryBase, JSTryNote) + 1 \ : 0; \ JS_END_MACRO extern void js_FinishTakingTryNotes(JSContext *cx, JSCodeGenerator *cg, JSTryNote *notes); JS_END_EXTERN_C #endif /* jsemit_h___ */ pacparser-1.4.5/src/spidermonkey/js/src/jsexn.c000066400000000000000000001260051464010763600215120ustar00rootroot00000000000000/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- * vim: set ts=8 sw=4 et tw=78: * * ***** BEGIN LICENSE BLOCK ***** * Version: MPL 1.1/GPL 2.0/LGPL 2.1 * * The contents of this file are subject to the Mozilla Public License Version * 1.1 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * http://www.mozilla.org/MPL/ * * Software distributed under the License is distributed on an "AS IS" basis, * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License * for the specific language governing rights and limitations under the * License. * * The Original Code is Mozilla Communicator client code, released * March 31, 1998. * * The Initial Developer of the Original Code is * Netscape Communications Corporation. * Portions created by the Initial Developer are Copyright (C) 1998 * the Initial Developer. All Rights Reserved. * * Contributor(s): * * Alternatively, the contents of this file may be used under the terms of * either of the GNU General Public License Version 2 or later (the "GPL"), * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), * in which case the provisions of the GPL or the LGPL are applicable instead * of those above. If you wish to allow use of your version of this file only * under the terms of either the GPL or the LGPL, and not to allow others to * use your version of this file under the terms of the MPL, indicate your * decision by deleting the provisions above and replace them with the notice * and other provisions required by the GPL or the LGPL. If you do not delete * the provisions above, a recipient may use your version of this file under * the terms of any one of the MPL, the GPL or the LGPL. * * ***** END LICENSE BLOCK ***** */ /* * JS standard exception implementation. */ #include "jsstddef.h" #include #include #include "jstypes.h" #include "jsbit.h" #include "jsutil.h" /* Added by JSIFY */ #include "jsprf.h" #include "jsapi.h" #include "jscntxt.h" #include "jsconfig.h" #include "jsdbgapi.h" #include "jsexn.h" #include "jsfun.h" #include "jsinterp.h" #include "jsnum.h" #include "jsopcode.h" #include "jsscript.h" /* Forward declarations for js_ErrorClass's initializer. */ static JSBool Exception(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval); static void exn_finalize(JSContext *cx, JSObject *obj); static uint32 exn_mark(JSContext *cx, JSObject *obj, void *arg); static void exn_finalize(JSContext *cx, JSObject *obj); static JSBool exn_enumerate(JSContext *cx, JSObject *obj); static JSBool exn_resolve(JSContext *cx, JSObject *obj, jsval id, uintN flags, JSObject **objp); JSClass js_ErrorClass = { js_Error_str, JSCLASS_HAS_PRIVATE | JSCLASS_NEW_RESOLVE | JSCLASS_HAS_CACHED_PROTO(JSProto_Error), JS_PropertyStub, JS_PropertyStub, JS_PropertyStub, JS_PropertyStub, exn_enumerate, (JSResolveOp)exn_resolve, JS_ConvertStub, exn_finalize, NULL, NULL, NULL, Exception, NULL, NULL, exn_mark, NULL }; typedef struct JSStackTraceElem { JSString *funName; size_t argc; const char *filename; uintN ulineno; } JSStackTraceElem; typedef struct JSExnPrivate { /* A copy of the JSErrorReport originally generated. */ JSErrorReport *errorReport; JSString *message; JSString *filename; uintN lineno; size_t stackDepth; JSStackTraceElem stackElems[1]; } JSExnPrivate; static JSString * StackTraceToString(JSContext *cx, JSExnPrivate *priv); static JSErrorReport * CopyErrorReport(JSContext *cx, JSErrorReport *report) { /* * We use a single malloc block to make a deep copy of JSErrorReport with * the following layout: * JSErrorReport * array of copies of report->messageArgs * jschar array with characters for all messageArgs * jschar array with characters for ucmessage * jschar array with characters for uclinebuf and uctokenptr * char array with characters for linebuf and tokenptr * char array with characters for filename * Such layout together with the properties enforced by the following * asserts does not need any extra alignment padding. */ JS_STATIC_ASSERT(sizeof(JSErrorReport) % sizeof(const char *) == 0); JS_STATIC_ASSERT(sizeof(const char *) % sizeof(jschar) == 0); size_t filenameSize; size_t linebufSize; size_t uclinebufSize; size_t ucmessageSize; size_t i, argsArraySize, argsCopySize, argSize; size_t mallocSize; JSErrorReport *copy; uint8 *cursor; #define JS_CHARS_SIZE(jschars) ((js_strlen(jschars) + 1) * sizeof(jschar)) filenameSize = report->filename ? strlen(report->filename) + 1 : 0; linebufSize = report->linebuf ? strlen(report->linebuf) + 1 : 0; uclinebufSize = report->uclinebuf ? JS_CHARS_SIZE(report->uclinebuf) : 0; ucmessageSize = 0; argsArraySize = 0; argsCopySize = 0; if (report->ucmessage) { ucmessageSize = JS_CHARS_SIZE(report->ucmessage); if (report->messageArgs) { for (i = 0; report->messageArgs[i]; ++i) argsCopySize += JS_CHARS_SIZE(report->messageArgs[i]); /* Non-null messageArgs should have at least one non-null arg. */ JS_ASSERT(i != 0); argsArraySize = (i + 1) * sizeof(const jschar *); } } /* * The mallocSize can not overflow since it represents the sum of the * sizes of already allocated objects. */ mallocSize = sizeof(JSErrorReport) + argsArraySize + argsCopySize + ucmessageSize + uclinebufSize + linebufSize + filenameSize; cursor = (uint8 *)JS_malloc(cx, mallocSize); if (!cursor) return NULL; copy = (JSErrorReport *)cursor; memset(cursor, 0, sizeof(JSErrorReport)); cursor += sizeof(JSErrorReport); if (argsArraySize != 0) { copy->messageArgs = (const jschar **)cursor; cursor += argsArraySize; for (i = 0; report->messageArgs[i]; ++i) { copy->messageArgs[i] = (const jschar *)cursor; argSize = JS_CHARS_SIZE(report->messageArgs[i]); memcpy(cursor, report->messageArgs[i], argSize); cursor += argSize; } copy->messageArgs[i] = NULL; JS_ASSERT(cursor == (uint8 *)copy->messageArgs[0] + argsCopySize); } if (report->ucmessage) { copy->ucmessage = (const jschar *)cursor; memcpy(cursor, report->ucmessage, ucmessageSize); cursor += ucmessageSize; } if (report->uclinebuf) { copy->uclinebuf = (const jschar *)cursor; memcpy(cursor, report->uclinebuf, uclinebufSize); cursor += uclinebufSize; if (report->uctokenptr) { copy->uctokenptr = copy->uclinebuf + (report->uctokenptr - report->uclinebuf); } } if (report->linebuf) { copy->linebuf = (const char *)cursor; memcpy(cursor, report->linebuf, linebufSize); cursor += linebufSize; if (report->tokenptr) { copy->tokenptr = copy->linebuf + (report->tokenptr - report->linebuf); } } if (report->filename) { copy->filename = (const char *)cursor; memcpy(cursor, report->filename, filenameSize); } JS_ASSERT(cursor + filenameSize == (uint8 *)copy + mallocSize); /* Copy non-pointer members. */ copy->lineno = report->lineno; copy->errorNumber = report->errorNumber; /* Note that this is before it gets flagged with JSREPORT_EXCEPTION */ copy->flags = report->flags; #undef JS_CHARS_SIZE return copy; } static jsval * GetStackTraceValueBuffer(JSExnPrivate *priv) { /* * We use extra memory after JSExnPrivateInfo.stackElems to store jsvals * that helps to produce more informative stack traces. The following * assert allows us to assume that no gap after stackElems is necessary to * align the buffer properly. */ JS_STATIC_ASSERT(sizeof(JSStackTraceElem) % sizeof(jsval) == 0); return (jsval *)(priv->stackElems + priv->stackDepth); } static JSBool InitExnPrivate(JSContext *cx, JSObject *exnObject, JSString *message, JSString *filename, uintN lineno, JSErrorReport *report) { JSCheckAccessOp checkAccess; JSErrorReporter older; JSExceptionState *state; jsval callerid, v; JSStackFrame *fp, *fpstop; size_t stackDepth, valueCount, size; JSBool overflow; JSExnPrivate *priv; JSStackTraceElem *elem; jsval *values; JS_ASSERT(OBJ_GET_CLASS(cx, exnObject) == &js_ErrorClass); /* * Prepare stack trace data. * * Set aside any error reporter for cx and save its exception state * so we can suppress any checkAccess failures. Such failures should stop * the backtrace procedure, not result in a failure of this constructor. */ checkAccess = cx->runtime->checkObjectAccess; older = JS_SetErrorReporter(cx, NULL); state = JS_SaveExceptionState(cx); callerid = ATOM_KEY(cx->runtime->atomState.callerAtom); stackDepth = 0; valueCount = 0; for (fp = cx->fp; fp; fp = fp->down) { if (fp->fun && fp->argv) { if (checkAccess) { v = fp->argv[-2]; if (!JSVAL_IS_PRIMITIVE(v) && !checkAccess(cx, JSVAL_TO_OBJECT(v), callerid, JSACC_READ, &v /* ignored */)) { break; } } valueCount += fp->argc; } ++stackDepth; } JS_RestoreExceptionState(cx, state); JS_SetErrorReporter(cx, older); fpstop = fp; size = offsetof(JSExnPrivate, stackElems); overflow = (stackDepth > ((size_t)-1 - size) / sizeof(JSStackTraceElem)); size += stackDepth * sizeof(JSStackTraceElem); overflow |= (valueCount > ((size_t)-1 - size) / sizeof(jsval)); size += valueCount * sizeof(jsval); if (overflow) { JS_ReportOutOfMemory(cx); return JS_FALSE; } priv = (JSExnPrivate *)JS_malloc(cx, size); if (!priv) return JS_FALSE; /* * We initialize errorReport with a copy of report after setting the * private slot, to prevent GC accessing a junk value we clear the field * here. */ priv->errorReport = NULL; priv->message = message; priv->filename = filename; priv->lineno = lineno; priv->stackDepth = stackDepth; values = GetStackTraceValueBuffer(priv); elem = priv->stackElems; for (fp = cx->fp; fp != fpstop; fp = fp->down) { if (!fp->fun) { elem->funName = NULL; elem->argc = 0; } else { elem->funName = fp->fun->atom ? ATOM_TO_STRING(fp->fun->atom) : cx->runtime->emptyString; elem->argc = fp->argc; memcpy(values, fp->argv, fp->argc * sizeof(jsval)); values += fp->argc; } elem->ulineno = 0; elem->filename = NULL; if (fp->script) { elem->filename = fp->script->filename; if (fp->pc) elem->ulineno = js_PCToLineNumber(cx, fp->script, fp->pc); } ++elem; } JS_ASSERT(priv->stackElems + stackDepth == elem); JS_ASSERT(GetStackTraceValueBuffer(priv) + valueCount == values); OBJ_SET_SLOT(cx, exnObject, JSSLOT_PRIVATE, PRIVATE_TO_JSVAL(priv)); if (report) { /* * Construct a new copy of the error report struct. We can't use the * error report struct that was passed in, because it's allocated on * the stack, and also because it may point to transient data in the * JSTokenStream. */ priv->errorReport = CopyErrorReport(cx, report); if (!priv->errorReport) { /* The finalizer realeases priv since it is in the private slot. */ return JS_FALSE; } } return JS_TRUE; } static JSExnPrivate * GetExnPrivate(JSContext *cx, JSObject *obj) { jsval privateValue; JSExnPrivate *priv; JS_ASSERT(OBJ_GET_CLASS(cx, obj) == &js_ErrorClass); privateValue = OBJ_GET_SLOT(cx, obj, JSSLOT_PRIVATE); if (JSVAL_IS_VOID(privateValue)) return NULL; priv = (JSExnPrivate *)JSVAL_TO_PRIVATE(privateValue); JS_ASSERT(priv); return priv; } static uint32 exn_mark(JSContext *cx, JSObject *obj, void *arg) { JSExnPrivate *priv; JSStackTraceElem *elem; size_t vcount, i; jsval *vp, v; priv = GetExnPrivate(cx, obj); if (priv) { GC_MARK(cx, priv->message, "exception message"); GC_MARK(cx, priv->filename, "exception filename"); elem = priv->stackElems; for (vcount = i = 0; i != priv->stackDepth; ++i, ++elem) { if (elem->funName) GC_MARK(cx, elem->funName, "stack trace function name"); if (elem->filename) js_MarkScriptFilename(elem->filename); vcount += elem->argc; } vp = GetStackTraceValueBuffer(priv); for (i = 0; i != vcount; ++i, ++vp) { v = *vp; if (JSVAL_IS_GCTHING(v)) GC_MARK(cx, JSVAL_TO_GCTHING(v), "stack trace argument"); } } return 0; } static void exn_finalize(JSContext *cx, JSObject *obj) { JSExnPrivate *priv; priv = GetExnPrivate(cx, obj); if (priv) { if (priv->errorReport) JS_free(cx, priv->errorReport); JS_free(cx, priv); } } static JSBool exn_enumerate(JSContext *cx, JSObject *obj) { JSAtomState *atomState; uintN i; JSAtom *atom; JSObject *pobj; JSProperty *prop; JS_STATIC_ASSERT(sizeof(JSAtomState) <= (size_t)(uint16)-1); static const uint16 offsets[] = { (uint16)offsetof(JSAtomState, messageAtom), (uint16)offsetof(JSAtomState, fileNameAtom), (uint16)offsetof(JSAtomState, lineNumberAtom), (uint16)offsetof(JSAtomState, stackAtom), }; atomState = &cx->runtime->atomState; for (i = 0; i != JS_ARRAY_LENGTH(offsets); ++i) { atom = *(JSAtom **)((uint8 *)atomState + offsets[i]); if (!js_LookupProperty(cx, obj, ATOM_TO_JSID(atom), &pobj, &prop)) return JS_FALSE; if (prop) OBJ_DROP_PROPERTY(cx, pobj, prop); } return JS_TRUE; } static JSBool exn_resolve(JSContext *cx, JSObject *obj, jsval id, uintN flags, JSObject **objp) { JSExnPrivate *priv; JSString *str; JSAtom *atom; JSString *stack; const char *prop; jsval v; *objp = NULL; priv = GetExnPrivate(cx, obj); if (priv && JSVAL_IS_STRING(id)) { str = JSVAL_TO_STRING(id); atom = cx->runtime->atomState.messageAtom; if (str == ATOM_TO_STRING(atom)) { prop = js_message_str; v = STRING_TO_JSVAL(priv->message); goto define; } atom = cx->runtime->atomState.fileNameAtom; if (str == ATOM_TO_STRING(atom)) { prop = js_fileName_str; v = STRING_TO_JSVAL(priv->filename); goto define; } atom = cx->runtime->atomState.lineNumberAtom; if (str == ATOM_TO_STRING(atom)) { prop = js_lineNumber_str; v = INT_TO_JSVAL(priv->lineno); goto define; } atom = cx->runtime->atomState.stackAtom; if (str == ATOM_TO_STRING(atom)) { stack = StackTraceToString(cx, priv); if (!stack) return JS_FALSE; /* Allow to GC all things that were used to build stack trace. */ priv->stackDepth = 0; prop = js_stack_str; v = STRING_TO_JSVAL(stack); goto define; } } return JS_TRUE; define: if (!JS_DefineProperty(cx, obj, prop, v, NULL, NULL, JSPROP_ENUMERATE)) return JS_FALSE; *objp = obj; return JS_TRUE; } JSErrorReport * js_ErrorFromException(JSContext *cx, jsval exn) { JSObject *obj; JSExnPrivate *priv; if (JSVAL_IS_PRIMITIVE(exn)) return NULL; obj = JSVAL_TO_OBJECT(exn); if (OBJ_GET_CLASS(cx, obj) != &js_ErrorClass) return NULL; priv = GetExnPrivate(cx, obj); if (!priv) return NULL; return priv->errorReport; } struct JSExnSpec { int protoIndex; const char *name; JSProtoKey key; JSNative native; }; /* * All *Error constructors share the same JSClass, js_ErrorClass. But each * constructor function for an *Error class must have a distinct native 'call' * function pointer, in order for instanceof to work properly across multiple * standard class sets. See jsfun.c:fun_hasInstance. */ #define MAKE_EXCEPTION_CTOR(name) \ static JSBool \ name(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) \ { \ return Exception(cx, obj, argc, argv, rval); \ } MAKE_EXCEPTION_CTOR(Error) MAKE_EXCEPTION_CTOR(InternalError) MAKE_EXCEPTION_CTOR(EvalError) MAKE_EXCEPTION_CTOR(RangeError) MAKE_EXCEPTION_CTOR(ReferenceError) MAKE_EXCEPTION_CTOR(SyntaxError) MAKE_EXCEPTION_CTOR(TypeError) MAKE_EXCEPTION_CTOR(URIError) #undef MAKE_EXCEPTION_CTOR static struct JSExnSpec exceptions[] = { {JSEXN_NONE, js_Error_str, JSProto_Error, Error}, {JSEXN_ERR, js_InternalError_str, JSProto_InternalError, InternalError}, {JSEXN_ERR, js_EvalError_str, JSProto_EvalError, EvalError}, {JSEXN_ERR, js_RangeError_str, JSProto_RangeError, RangeError}, {JSEXN_ERR, js_ReferenceError_str, JSProto_ReferenceError, ReferenceError}, {JSEXN_ERR, js_SyntaxError_str, JSProto_SyntaxError, SyntaxError}, {JSEXN_ERR, js_TypeError_str, JSProto_TypeError, TypeError}, {JSEXN_ERR, js_URIError_str, JSProto_URIError, URIError}, {0, NULL, JSProto_Null, NULL} }; static JSString * ValueToShortSource(JSContext *cx, jsval v) { JSString *str; /* Avoid toSource bloat and fallibility for object types. */ if (JSVAL_IS_PRIMITIVE(v)) { str = js_ValueToSource(cx, v); } else if (VALUE_IS_FUNCTION(cx, v)) { /* * XXX Avoid function decompilation bloat for now. */ str = JS_GetFunctionId(JS_ValueToFunction(cx, v)); if (!str && !(str = js_ValueToSource(cx, v))) { /* * Continue to soldier on if the function couldn't be * converted into a string. */ JS_ClearPendingException(cx); str = JS_NewStringCopyZ(cx, "[unknown function]"); } } else { /* * XXX Avoid toString on objects, it takes too long and uses too much * memory, for too many classes (see Mozilla bug 166743). */ char buf[100]; JS_snprintf(buf, sizeof buf, "[object %s]", OBJ_GET_CLASS(cx, JSVAL_TO_OBJECT(v))->name); str = JS_NewStringCopyZ(cx, buf); } return str; } static JSString * StackTraceToString(JSContext *cx, JSExnPrivate *priv) { jschar *stackbuf; size_t stacklen, stackmax; JSStackTraceElem *elem, *endElem; jsval *values; size_t i; JSString *str; const char *cp; char ulnbuf[11]; /* After this point, failing control flow must goto bad. */ stackbuf = NULL; stacklen = stackmax = 0; /* Limit the stackbuf length to a reasonable value to avoid overflow checks. */ #define STACK_LENGTH_LIMIT JS_BIT(20) #define APPEND_CHAR_TO_STACK(c) \ JS_BEGIN_MACRO \ if (stacklen == stackmax) { \ void *ptr_; \ if (stackmax >= STACK_LENGTH_LIMIT) \ goto done; \ stackmax = stackmax ? 2 * stackmax : 64; \ ptr_ = JS_realloc(cx, stackbuf, (stackmax+1) * sizeof(jschar)); \ if (!ptr_) \ goto bad; \ stackbuf = ptr_; \ } \ stackbuf[stacklen++] = (c); \ JS_END_MACRO #define APPEND_STRING_TO_STACK(str) \ JS_BEGIN_MACRO \ JSString *str_ = str; \ size_t length_ = JSSTRING_LENGTH(str_); \ if (length_ > stackmax - stacklen) { \ void *ptr_; \ if (stackmax >= STACK_LENGTH_LIMIT || \ length_ >= STACK_LENGTH_LIMIT - stacklen) { \ goto done; \ } \ stackmax = JS_BIT(JS_CeilingLog2(stacklen + length_)); \ ptr_ = JS_realloc(cx, stackbuf, (stackmax+1) * sizeof(jschar)); \ if (!ptr_) \ goto bad; \ stackbuf = ptr_; \ } \ js_strncpy(stackbuf + stacklen, JSSTRING_CHARS(str_), length_); \ stacklen += length_; \ JS_END_MACRO values = GetStackTraceValueBuffer(priv); elem = priv->stackElems; for (endElem = elem + priv->stackDepth; elem != endElem; elem++) { if (elem->funName) { APPEND_STRING_TO_STACK(elem->funName); APPEND_CHAR_TO_STACK('('); for (i = 0; i != elem->argc; i++, values++) { if (i > 0) APPEND_CHAR_TO_STACK(','); str = ValueToShortSource(cx, *values); if (!str) goto bad; APPEND_STRING_TO_STACK(str); } APPEND_CHAR_TO_STACK(')'); } APPEND_CHAR_TO_STACK('@'); if (elem->filename) { for (cp = elem->filename; *cp; cp++) APPEND_CHAR_TO_STACK(*cp); } APPEND_CHAR_TO_STACK(':'); JS_snprintf(ulnbuf, sizeof ulnbuf, "%u", elem->ulineno); for (cp = ulnbuf; *cp; cp++) APPEND_CHAR_TO_STACK(*cp); APPEND_CHAR_TO_STACK('\n'); } #undef APPEND_CHAR_TO_STACK #undef APPEND_STRING_TO_STACK #undef STACK_LENGTH_LIMIT done: if (stacklen == 0) { JS_ASSERT(!stackbuf); return cx->runtime->emptyString; } if (stacklen < stackmax) { /* * Realloc can fail when shrinking on some FreeBSD versions, so * don't use JS_realloc here; simply let the oversized allocation * be owned by the string in that rare case. */ void *shrunk = JS_realloc(cx, stackbuf, (stacklen+1) * sizeof(jschar)); if (shrunk) stackbuf = shrunk; } stackbuf[stacklen] = 0; str = js_NewString(cx, stackbuf, stacklen, 0); if (str) return str; bad: if (stackbuf) JS_free(cx, stackbuf); return NULL; } /* XXXbe Consolidate the ugly truth that we don't treat filename as UTF-8 with these two functions. */ static JSString * FilenameToString(JSContext *cx, const char *filename) { return JS_NewStringCopyZ(cx, filename); } static const char * StringToFilename(JSContext *cx, JSString *str) { return JS_GetStringBytes(str); } static JSBool Exception(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) { JSBool ok; uint32 lineno; JSString *message, *filename; JSStackFrame *fp; if (cx->creatingException) return JS_FALSE; cx->creatingException = JS_TRUE; if (!(cx->fp->flags & JSFRAME_CONSTRUCTING)) { /* * ECMA ed. 3, 15.11.1 requires Error, etc., to construct even when * called as functions, without operator new. But as we do not give * each constructor a distinct JSClass, whose .name member is used by * js_NewObject to find the class prototype, we must get the class * prototype ourselves. */ ok = OBJ_GET_PROPERTY(cx, JSVAL_TO_OBJECT(argv[-2]), ATOM_TO_JSID(cx->runtime->atomState .classPrototypeAtom), rval); if (!ok) goto out; obj = js_NewObject(cx, &js_ErrorClass, JSVAL_TO_OBJECT(*rval), NULL); if (!obj) { ok = JS_FALSE; goto out; } *rval = OBJECT_TO_JSVAL(obj); } /* * If it's a new object of class Exception, then null out the private * data so that the finalizer doesn't attempt to free it. */ if (OBJ_GET_CLASS(cx, obj) == &js_ErrorClass) OBJ_SET_SLOT(cx, obj, JSSLOT_PRIVATE, JSVAL_VOID); /* Set the 'message' property. */ if (argc != 0) { message = js_ValueToString(cx, argv[0]); if (!message) { ok = JS_FALSE; goto out; } argv[0] = STRING_TO_JSVAL(message); } else { message = cx->runtime->emptyString; } /* Set the 'fileName' property. */ if (argc > 1) { filename = js_ValueToString(cx, argv[1]); if (!filename) { ok = JS_FALSE; goto out; } argv[1] = STRING_TO_JSVAL(filename); fp = NULL; } else { fp = JS_GetScriptedCaller(cx, NULL); if (fp) { filename = FilenameToString(cx, fp->script->filename); if (!filename) { ok = JS_FALSE; goto out; } } else { filename = cx->runtime->emptyString; } } /* Set the 'lineNumber' property. */ if (argc > 2) { ok = js_ValueToECMAUint32(cx, argv[2], &lineno); if (!ok) goto out; } else { if (!fp) fp = JS_GetScriptedCaller(cx, NULL); lineno = (fp && fp->pc) ? js_PCToLineNumber(cx, fp->script, fp->pc) : 0; } ok = (OBJ_GET_CLASS(cx, obj) != &js_ErrorClass) || InitExnPrivate(cx, obj, message, filename, lineno, NULL); out: cx->creatingException = JS_FALSE; return ok; } /* * Convert to string. * * This method only uses JavaScript-modifiable properties name, message. It * is left to the host to check for private data and report filename and line * number information along with this message. */ static JSBool exn_toString(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) { jsval v; JSString *name, *message, *result; jschar *chars, *cp; size_t name_length, message_length, length; if (!OBJ_GET_PROPERTY(cx, obj, ATOM_TO_JSID(cx->runtime->atomState.nameAtom), &v)) { return JS_FALSE; } name = JSVAL_IS_STRING(v) ? JSVAL_TO_STRING(v) : cx->runtime->emptyString; *rval = STRING_TO_JSVAL(name); if (!JS_GetProperty(cx, obj, js_message_str, &v)) return JS_FALSE; message = JSVAL_IS_STRING(v) ? JSVAL_TO_STRING(v) : cx->runtime->emptyString; if (JSSTRING_LENGTH(message) != 0) { name_length = JSSTRING_LENGTH(name); message_length = JSSTRING_LENGTH(message); length = (name_length ? name_length + 2 : 0) + message_length; cp = chars = (jschar*) JS_malloc(cx, (length + 1) * sizeof(jschar)); if (!chars) return JS_FALSE; if (name_length) { js_strncpy(cp, JSSTRING_CHARS(name), name_length); cp += name_length; *cp++ = ':'; *cp++ = ' '; } js_strncpy(cp, JSSTRING_CHARS(message), message_length); cp += message_length; *cp = 0; result = js_NewString(cx, chars, length, 0); if (!result) { JS_free(cx, chars); return JS_FALSE; } } else { result = name; } *rval = STRING_TO_JSVAL(result); return JS_TRUE; } #if JS_HAS_TOSOURCE /* * Return a string that may eval to something similar to the original object. */ static JSBool exn_toSource(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) { jsval *vp; JSString *name, *message, *filename, *lineno_as_str, *result; uint32 lineno; size_t lineno_length, name_length, message_length, filename_length, length; jschar *chars, *cp; vp = argv + argc; /* beginning of explicit local roots */ if (!OBJ_GET_PROPERTY(cx, obj, ATOM_TO_JSID(cx->runtime->atomState.nameAtom), rval)) { return JS_FALSE; } name = js_ValueToString(cx, *rval); if (!name) return JS_FALSE; *rval = STRING_TO_JSVAL(name); if (!JS_GetProperty(cx, obj, js_message_str, &vp[0]) || !(message = js_ValueToSource(cx, vp[0]))) { return JS_FALSE; } vp[0] = STRING_TO_JSVAL(message); if (!JS_GetProperty(cx, obj, js_fileName_str, &vp[1]) || !(filename = js_ValueToSource(cx, vp[1]))) { return JS_FALSE; } vp[1] = STRING_TO_JSVAL(filename); if (!JS_GetProperty(cx, obj, js_lineNumber_str, &vp[2]) || !js_ValueToECMAUint32 (cx, vp[2], &lineno)) { return JS_FALSE; } if (lineno != 0) { lineno_as_str = js_ValueToString(cx, vp[2]); if (!lineno_as_str) return JS_FALSE; lineno_length = JSSTRING_LENGTH(lineno_as_str); } else { lineno_as_str = NULL; lineno_length = 0; } /* Magic 8, for the characters in ``(new ())''. */ name_length = JSSTRING_LENGTH(name); message_length = JSSTRING_LENGTH(message); length = 8 + name_length + message_length; filename_length = JSSTRING_LENGTH(filename); if (filename_length != 0) { /* append filename as ``, {filename}'' */ length += 2 + filename_length; if (lineno_as_str) { /* append lineno as ``, {lineno_as_str}'' */ length += 2 + lineno_length; } } else { if (lineno_as_str) { /* * no filename, but have line number, * need to append ``, "", {lineno_as_str}'' */ length += 6 + lineno_length; } } cp = chars = (jschar*) JS_malloc(cx, (length + 1) * sizeof(jschar)); if (!chars) return JS_FALSE; *cp++ = '('; *cp++ = 'n'; *cp++ = 'e'; *cp++ = 'w'; *cp++ = ' '; js_strncpy(cp, JSSTRING_CHARS(name), name_length); cp += name_length; *cp++ = '('; if (message_length != 0) { js_strncpy(cp, JSSTRING_CHARS(message), message_length); cp += message_length; } if (filename_length != 0) { /* append filename as ``, {filename}'' */ *cp++ = ','; *cp++ = ' '; js_strncpy(cp, JSSTRING_CHARS(filename), filename_length); cp += filename_length; } else { if (lineno_as_str) { /* * no filename, but have line number, * need to append ``, "", {lineno_as_str}'' */ *cp++ = ','; *cp++ = ' '; *cp++ = '"'; *cp++ = '"'; } } if (lineno_as_str) { /* append lineno as ``, {lineno_as_str}'' */ *cp++ = ','; *cp++ = ' '; js_strncpy(cp, JSSTRING_CHARS(lineno_as_str), lineno_length); cp += lineno_length; } *cp++ = ')'; *cp++ = ')'; *cp = 0; result = js_NewString(cx, chars, length, 0); if (!result) { JS_free(cx, chars); return JS_FALSE; } *rval = STRING_TO_JSVAL(result); return JS_TRUE; } #endif static JSFunctionSpec exception_methods[] = { #if JS_HAS_TOSOURCE {js_toSource_str, exn_toSource, 0,0,3}, #endif {js_toString_str, exn_toString, 0,0,0}, {0,0,0,0,0} }; JSObject * js_InitExceptionClasses(JSContext *cx, JSObject *obj) { JSObject *obj_proto, *protos[JSEXN_LIMIT]; int i; /* * If lazy class initialization occurs for any Error subclass, then all * classes are initialized, starting with Error. To avoid reentry and * redundant initialization, we must not pass a null proto parameter to * js_NewObject below, when called for the Error superclass. We need to * ensure that Object.prototype is the proto of Error.prototype. * * See the equivalent code to ensure that parent_proto is non-null when * JS_InitClass calls js_NewObject, in jsapi.c. */ if (!js_GetClassPrototype(cx, obj, INT_TO_JSID(JSProto_Object), &obj_proto)) { return NULL; } if (!js_EnterLocalRootScope(cx)) return NULL; /* Initialize the prototypes first. */ for (i = 0; exceptions[i].name != 0; i++) { JSAtom *atom; JSFunction *fun; JSObject *funobj; JSString *nameString; int protoIndex = exceptions[i].protoIndex; /* Make the prototype for the current constructor name. */ protos[i] = js_NewObject(cx, &js_ErrorClass, (protoIndex != JSEXN_NONE) ? protos[protoIndex] : obj_proto, obj); if (!protos[i]) break; /* So exn_finalize knows whether to destroy private data. */ OBJ_SET_SLOT(cx, protos[i], JSSLOT_PRIVATE, JSVAL_VOID); /* Make a constructor function for the current name. */ atom = cx->runtime->atomState.classAtoms[exceptions[i].key]; fun = js_DefineFunction(cx, obj, atom, exceptions[i].native, 3, 0); if (!fun) break; /* Make this constructor make objects of class Exception. */ fun->clasp = &js_ErrorClass; /* Extract the constructor object. */ funobj = fun->object; /* Make the prototype and constructor links. */ if (!js_SetClassPrototype(cx, funobj, protos[i], JSPROP_READONLY | JSPROP_PERMANENT)) { break; } /* proto bootstrap bit from JS_InitClass omitted. */ nameString = JS_NewStringCopyZ(cx, exceptions[i].name); if (!nameString) break; /* Add the name property to the prototype. */ if (!JS_DefineProperty(cx, protos[i], js_name_str, STRING_TO_JSVAL(nameString), NULL, NULL, JSPROP_ENUMERATE)) { break; } /* Finally, stash the constructor for later uses. */ if (!js_SetClassObject(cx, obj, exceptions[i].key, funobj)) break; } js_LeaveLocalRootScope(cx); if (exceptions[i].name) return NULL; /* * Add an empty message property. (To Exception.prototype only, * because this property will be the same for all the exception * protos.) */ if (!JS_DefineProperty(cx, protos[0], js_message_str, STRING_TO_JSVAL(cx->runtime->emptyString), NULL, NULL, JSPROP_ENUMERATE)) { return NULL; } if (!JS_DefineProperty(cx, protos[0], js_fileName_str, STRING_TO_JSVAL(cx->runtime->emptyString), NULL, NULL, JSPROP_ENUMERATE)) { return NULL; } if (!JS_DefineProperty(cx, protos[0], js_lineNumber_str, INT_TO_JSVAL(0), NULL, NULL, JSPROP_ENUMERATE)) { return NULL; } /* * Add methods only to Exception.prototype, because ostensibly all * exception types delegate to that. */ if (!JS_DefineFunctions(cx, protos[0], exception_methods)) return NULL; return protos[0]; } const JSErrorFormatString* js_GetLocalizedErrorMessage(JSContext* cx, void *userRef, const char *locale, const uintN errorNumber) { const JSErrorFormatString *errorString = NULL; if (cx->localeCallbacks && cx->localeCallbacks->localeGetErrorMessage) { errorString = cx->localeCallbacks ->localeGetErrorMessage(userRef, locale, errorNumber); } if (!errorString) errorString = js_GetErrorMessage(userRef, locale, errorNumber); return errorString; } #if defined ( DEBUG_mccabe ) && defined ( PRINTNAMES ) /* For use below... get character strings for error name and exception name */ static struct exnname { char *name; char *exception; } errortoexnname[] = { #define MSG_DEF(name, number, count, exception, format) \ {#name, #exception}, #include "js.msg" #undef MSG_DEF }; #endif /* DEBUG */ JSBool js_ErrorToException(JSContext *cx, const char *message, JSErrorReport *reportp) { JSErrNum errorNumber; const JSErrorFormatString *errorString; JSExnType exn; jsval tv[4]; JSTempValueRooter tvr; JSBool ok; JSObject *errProto, *errObject; JSString *messageStr, *filenameStr; /* * Tell our caller to report immediately if cx has no active frames, or if * this report is just a warning. */ JS_ASSERT(reportp); if (!cx->fp || JSREPORT_IS_WARNING(reportp->flags)) return JS_FALSE; /* Find the exception index associated with this error. */ errorNumber = (JSErrNum) reportp->errorNumber; errorString = js_GetLocalizedErrorMessage(cx, NULL, NULL, errorNumber); exn = errorString ? errorString->exnType : JSEXN_NONE; JS_ASSERT(exn < JSEXN_LIMIT); #if defined( DEBUG_mccabe ) && defined ( PRINTNAMES ) /* Print the error name and the associated exception name to stderr */ fprintf(stderr, "%s\t%s\n", errortoexnname[errorNumber].name, errortoexnname[errorNumber].exception); #endif /* * Return false (no exception raised) if no exception is associated * with the given error number. */ if (exn == JSEXN_NONE) return JS_FALSE; /* * Prevent runaway recursion, just as the Exception native constructor * must do, via cx->creatingException. If an out-of-memory error occurs, * no exception object will be created, but we don't assume that OOM is * the only kind of error that subroutines of this function called below * might raise. */ if (cx->creatingException) return JS_FALSE; /* After this point the control must flow through the label out. */ cx->creatingException = JS_TRUE; /* Protect the newly-created strings below from nesting GCs. */ memset(tv, 0, sizeof tv); JS_PUSH_TEMP_ROOT(cx, sizeof tv / sizeof tv[0], tv, &tvr); /* * Try to get an appropriate prototype by looking up the corresponding * exception constructor name in the scope chain of the current context's * top stack frame, or in the global object if no frame is active. */ ok = js_GetClassPrototype(cx, NULL, INT_TO_JSID(exceptions[exn].key), &errProto); if (!ok) goto out; tv[0] = OBJECT_TO_JSVAL(errProto); errObject = js_NewObject(cx, &js_ErrorClass, errProto, NULL); if (!errObject) { ok = JS_FALSE; goto out; } tv[1] = OBJECT_TO_JSVAL(errObject); messageStr = JS_NewStringCopyZ(cx, message); if (!messageStr) { ok = JS_FALSE; goto out; } tv[2] = STRING_TO_JSVAL(messageStr); filenameStr = JS_NewStringCopyZ(cx, reportp->filename); if (!filenameStr) { ok = JS_FALSE; goto out; } tv[3] = STRING_TO_JSVAL(filenameStr); ok = InitExnPrivate(cx, errObject, messageStr, filenameStr, reportp->lineno, reportp); if (!ok) goto out; JS_SetPendingException(cx, OBJECT_TO_JSVAL(errObject)); /* Flag the error report passed in to indicate an exception was raised. */ reportp->flags |= JSREPORT_EXCEPTION; out: JS_POP_TEMP_ROOT(cx, &tvr); cx->creatingException = JS_FALSE; return ok; } JSBool js_ReportUncaughtException(JSContext *cx) { jsval exn; JSObject *exnObject; jsval vp[5]; JSTempValueRooter tvr; JSErrorReport *reportp, report; JSString *str; const char *bytes; JSBool ok; if (!JS_IsExceptionPending(cx)) return JS_TRUE; if (!JS_GetPendingException(cx, &exn)) return JS_FALSE; /* * Because js_ValueToString below could error and an exception object * could become unrooted, we must root exnObject. Later, if exnObject is * non-null, we need to root other intermediates, so allocate an operand * stack segment to protect all of these values. */ if (JSVAL_IS_PRIMITIVE(exn)) { exnObject = NULL; } else { exnObject = JSVAL_TO_OBJECT(exn); vp[0] = exn; memset(vp + 1, 0, sizeof vp - sizeof vp[0]); JS_PUSH_TEMP_ROOT(cx, JS_ARRAY_LENGTH(vp), vp, &tvr); } JS_ClearPendingException(cx); reportp = js_ErrorFromException(cx, exn); /* XXX L10N angels cry once again (see also jsemit.c, /L10N gaffes/) */ str = js_ValueToString(cx, exn); if (!str) { bytes = "unknown (can't convert to string)"; } else { if (exnObject) vp[1] = STRING_TO_JSVAL(str); bytes = js_GetStringBytes(cx->runtime, str); } ok = JS_TRUE; if (!reportp && exnObject && OBJ_GET_CLASS(cx, exnObject) == &js_ErrorClass) { const char *filename; uint32 lineno; ok = JS_GetProperty(cx, exnObject, js_message_str, &vp[2]); if (!ok) goto out; if (JSVAL_IS_STRING(vp[2])) bytes = JS_GetStringBytes(JSVAL_TO_STRING(vp[2])); ok = JS_GetProperty(cx, exnObject, js_fileName_str, &vp[3]); if (!ok) goto out; str = js_ValueToString(cx, vp[3]); if (!str) { ok = JS_FALSE; goto out; } filename = StringToFilename(cx, str); ok = JS_GetProperty(cx, exnObject, js_lineNumber_str, &vp[4]); if (!ok) goto out; ok = js_ValueToECMAUint32 (cx, vp[4], &lineno); if (!ok) goto out; reportp = &report; memset(&report, 0, sizeof report); report.filename = filename; report.lineno = (uintN) lineno; } if (!reportp) { JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_UNCAUGHT_EXCEPTION, bytes); } else { /* Flag the error as an exception. */ reportp->flags |= JSREPORT_EXCEPTION; js_ReportErrorAgain(cx, bytes, reportp); } out: if (exnObject) JS_POP_TEMP_ROOT(cx, &tvr); return ok; } pacparser-1.4.5/src/spidermonkey/js/src/jsexn.h000066400000000000000000000072731464010763600215240ustar00rootroot00000000000000/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- * * ***** BEGIN LICENSE BLOCK ***** * Version: MPL 1.1/GPL 2.0/LGPL 2.1 * * The contents of this file are subject to the Mozilla Public License Version * 1.1 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * http://www.mozilla.org/MPL/ * * Software distributed under the License is distributed on an "AS IS" basis, * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License * for the specific language governing rights and limitations under the * License. * * The Original Code is Mozilla Communicator client code, released * March 31, 1998. * * The Initial Developer of the Original Code is * Netscape Communications Corporation. * Portions created by the Initial Developer are Copyright (C) 1998 * the Initial Developer. All Rights Reserved. * * Contributor(s): * * Alternatively, the contents of this file may be used under the terms of * either of the GNU General Public License Version 2 or later (the "GPL"), * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), * in which case the provisions of the GPL or the LGPL are applicable instead * of those above. If you wish to allow use of your version of this file only * under the terms of either the GPL or the LGPL, and not to allow others to * use your version of this file under the terms of the MPL, indicate your * decision by deleting the provisions above and replace them with the notice * and other provisions required by the GPL or the LGPL. If you do not delete * the provisions above, a recipient may use your version of this file under * the terms of any one of the MPL, the GPL or the LGPL. * * ***** END LICENSE BLOCK ***** */ /* * JS runtime exception classes. */ #ifndef jsexn_h___ #define jsexn_h___ JS_BEGIN_EXTERN_C extern JSClass js_ErrorClass; /* * Initialize the exception constructor/prototype hierarchy. */ extern JSObject * js_InitExceptionClasses(JSContext *cx, JSObject *obj); /* * Given a JSErrorReport, check to see if there is an exception associated with * the error number. If there is, then create an appropriate exception object, * set it as the pending exception, and set the JSREPORT_EXCEPTION flag on the * error report. Exception-aware host error reporters should probably ignore * error reports so flagged. Returns JS_TRUE if an associated exception is * found and set, JS_FALSE otherwise.. */ extern JSBool js_ErrorToException(JSContext *cx, const char *message, JSErrorReport *reportp); /* * Called if a JS API call to js_Execute or js_InternalCall fails; calls the * error reporter with the error report associated with any uncaught exception * that has been raised. Returns true if there was an exception pending, and * the error reporter was actually called. * * The JSErrorReport * that the error reporter is called with is currently * associated with a JavaScript object, and is not guaranteed to persist after * the object is collected. Any persistent uses of the JSErrorReport contents * should make their own copy. * * The flags field of the JSErrorReport will have the JSREPORT_EXCEPTION flag * set; embeddings that want to silently propagate JavaScript exceptions to * other contexts may want to use an error reporter that ignores errors with * this flag. */ extern JSBool js_ReportUncaughtException(JSContext *cx); extern JSErrorReport * js_ErrorFromException(JSContext *cx, jsval exn); extern const JSErrorFormatString * js_GetLocalizedErrorMessage(JSContext* cx, void *userRef, const char *locale, const uintN errorNumber); JS_END_EXTERN_C #endif /* jsexn_h___ */ pacparser-1.4.5/src/spidermonkey/js/src/jsfile.c000066400000000000000000002407761464010763600216530ustar00rootroot00000000000000/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- * vim: set ts=8 sw=4 et tw=80: * * ***** BEGIN LICENSE BLOCK ***** * Version: MPL 1.1/GPL 2.0/LGPL 2.1 * * The contents of this file are subject to the Mozilla Public License Version * 1.1 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * http://www.mozilla.org/MPL/ * * Software distributed under the License is distributed on an "AS IS" basis, * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License * for the specific language governing rights and limitations under the * License. * * The Original Code is Mozilla Communicator client code, released * March 31, 1998. * * The Initial Developer of the Original Code is * Netscape Communications Corporation. * Portions created by the Initial Developer are Copyright (C) 1998 * the Initial Developer. All Rights Reserved. * * Contributor(s): * * Alternatively, the contents of this file may be used under the terms of * either of the GNU General Public License Version 2 or later (the "GPL"), * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), * in which case the provisions of the GPL or the LGPL are applicable instead * of those above. If you wish to allow use of your version of this file only * under the terms of either the GPL or the LGPL, and not to allow others to * use your version of this file under the terms of the MPL, indicate your * decision by deleting the provisions above and replace them with the notice * and other provisions required by the GPL or the LGPL. If you do not delete * the provisions above, a recipient may use your version of this file under * the terms of any one of the MPL, the GPL or the LGPL. * * ***** END LICENSE BLOCK ***** */ /* * JS File object */ #if JS_HAS_FILE_OBJECT #include "jsstddef.h" #include "jsfile.h" /* ----------------- Platform-specific includes and defines ----------------- */ #if defined(XP_WIN) || defined(XP_OS2) # include # include # include # include # define FILESEPARATOR '\\' # define FILESEPARATOR2 '/' # define CURRENT_DIR "c:\\" # define POPEN _popen # define PCLOSE _pclose #elif defined(XP_UNIX) || defined(XP_BEOS) # include # include # include # include # define FILESEPARATOR '/' # define FILESEPARATOR2 '\0' # define CURRENT_DIR "/" # define POPEN popen # define PCLOSE pclose #endif /* --------------- Platform-independent includes and defines ---------------- */ #include "jsapi.h" #include "jsatom.h" #include "jscntxt.h" #include "jsdate.h" #include "jsdbgapi.h" #include "jsemit.h" #include "jsfun.h" #include "jslock.h" #include "jsobj.h" #include "jsparse.h" #include "jsscan.h" #include "jsscope.h" #include "jsscript.h" #include "jsstr.h" #include "jsutil.h" /* Added by JSIFY */ #include /* NSPR dependencies */ #include "prio.h" #include "prerror.h" #define SPECIAL_FILE_STRING "Special File" #define CURRENTDIR_PROPERTY "currentDir" #define SEPARATOR_PROPERTY "separator" #define FILE_CONSTRUCTOR "File" #define PIPE_SYMBOL '|' #define ASCII 0 #define UTF8 1 #define UCS2 2 #define asciistring "text" #define utfstring "binary" #define unicodestring "unicode" #define MAX_PATH_LENGTH 1024 #define MODE_SIZE 256 #define NUMBER_SIZE 32 #define MAX_LINE_LENGTH 256 #define URL_PREFIX "file://" #define STDINPUT_NAME "Standard input stream" #define STDOUTPUT_NAME "Standard output stream" #define STDERROR_NAME "Standard error stream" #define RESOLVE_PATH js_canonicalPath /* js_absolutePath */ /* Error handling */ typedef enum JSFileErrNum { #define MSG_DEF(name, number, count, exception, format) \ name = number, #include "jsfile.msg" #undef MSG_DEF JSFileErr_Limit #undef MSGDEF } JSFileErrNum; #define JSFILE_HAS_DFLT_MSG_STRINGS 1 JSErrorFormatString JSFile_ErrorFormatString[JSFileErr_Limit] = { #if JSFILE_HAS_DFLT_MSG_STRINGS #define MSG_DEF(name, number, count, exception, format) \ { format, count }, #else #define MSG_DEF(name, number, count, exception, format) \ { NULL, count }, #endif #include "jsfile.msg" #undef MSG_DEF }; const JSErrorFormatString * JSFile_GetErrorMessage(void *userRef, const char *locale, const uintN errorNumber) { if ((errorNumber > 0) && (errorNumber < JSFileErr_Limit)) return &JSFile_ErrorFormatString[errorNumber]; else return NULL; } #define JSFILE_CHECK_NATIVE(op) \ if (file->isNative) { \ JS_ReportWarning(cx, "Cannot call or access \"%s\" on native file %s",\ op, file->path); \ goto out; \ } #define JSFILE_CHECK_WRITE \ if (!file->isOpen) { \ JS_ReportWarning(cx, \ "File %s is closed, will open it for writing, proceeding", \ file->path); \ js_FileOpen(cx, obj, file, "write,append,create"); \ } \ if (!js_canWrite(cx, file)) { \ JS_ReportErrorNumber(cx, JSFile_GetErrorMessage, NULL, \ JSFILEMSG_CANNOT_WRITE, file->path); \ goto out; \ } #define JSFILE_CHECK_READ \ if (!file->isOpen) { \ JS_ReportWarning(cx, \ "File %s is closed, will open it for reading, proceeding", \ file->path); \ js_FileOpen(cx, obj, file, "read"); \ } \ if (!js_canRead(cx, file)) { \ JS_ReportErrorNumber(cx, JSFile_GetErrorMessage, NULL, \ JSFILEMSG_CANNOT_READ, file->path); \ goto out; \ } #define JSFILE_CHECK_OPEN(op) \ if (!file->isOpen) { \ JS_ReportErrorNumber(cx, JSFile_GetErrorMessage, NULL, \ JSFILEMSG_FILE_MUST_BE_CLOSED, op); \ goto out; \ } #define JSFILE_CHECK_CLOSED(op) \ if (file->isOpen) { \ JS_ReportErrorNumber(cx, JSFile_GetErrorMessage, NULL, \ JSFILEMSG_FILE_MUST_BE_OPEN, op); \ goto out; \ } #define JSFILE_CHECK_ONE_ARG(op) \ if (argc != 1) { \ char str[NUMBER_SIZE]; \ sprintf(str, "%d", argc); \ JS_ReportErrorNumber(cx, JSFile_GetErrorMessage, NULL, \ JSFILEMSG_EXPECTS_ONE_ARG_ERROR, op, str); \ goto out; \ } /* Security mechanism, should define a callback for this. The parameters are as follows: SECURITY_CHECK(JSContext *cx, JSPrincipals *ps, char *op_name, JSFile *file) XXX Should this be a real function returning a JSBool result (and getting some typesafety help from the compiler?). */ #define SECURITY_CHECK(cx, ps, op, file) \ /* Define a callback here... */ /* Structure representing the file internally */ typedef struct JSFile { char *path; /* the path to the file. */ JSBool isOpen; int32 mode; /* mode used to open the file: read, write, append, create, etc.. */ int32 type; /* Asciiz, utf, unicode */ char byteBuffer[3]; /* bytes read in advance by js_FileRead ( UTF8 encoding ) */ jsint nbBytesInBuf; /* number of bytes stored in the buffer above */ jschar charBuffer; /* character read in advance by readln ( mac files only ) */ JSBool charBufferUsed; /* flag indicating if the buffer above is being used */ JSBool hasRandomAccess;/* can the file be randomly accessed? false for stdin, and UTF-encoded files. */ JSBool hasAutoflush; /* should we force a flush for each line break? */ JSBool isNative; /* if the file is using OS-specific file FILE type */ /* We can actually put the following two in a union since they should never be used at the same time */ PRFileDesc *handle; /* the handle for the file, if open. */ FILE *nativehandle; /* native handle, for stuff NSPR doesn't do. */ JSBool isPipe; /* if the file is really an OS pipe */ } JSFile; /* a few forward declarations... */ JS_PUBLIC_API(JSObject*) js_NewFileObject(JSContext *cx, char *filename); static JSBool file_open(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval); static JSBool file_close(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval); /* New filename manipulation procesures */ /* assumes we don't have leading/trailing spaces */ static JSBool js_filenameHasAPipe(const char *filename) { if (!filename) return JS_FALSE; return filename[0] == PIPE_SYMBOL || filename[strlen(filename) - 1] == PIPE_SYMBOL; } static JSBool js_isAbsolute(const char *name) { #if defined(XP_WIN) || defined(XP_OS2) return *name && name[1] == ':'; #else return (name[0] # if defined(XP_UNIX) || defined(XP_BEOS) == # else != # endif FILESEPARATOR); #endif } /* * Concatinates base and name to produce a valid filename. * Returned string must be freed. */ static char* js_combinePath(JSContext *cx, const char *base, const char *name) { int len = strlen(base); char* result = JS_malloc(cx, len + strlen(name) + 2); if (!result) return NULL; strcpy(result, base); if (base[len - 1] != FILESEPARATOR && base[len - 1] != FILESEPARATOR2) { result[len] = FILESEPARATOR; result[len + 1] = '\0'; } strcat(result, name); return result; } /* Extract the last component from a path name. Returned string must be freed */ static char * js_fileBaseName(JSContext *cx, const char *pathname) { jsint index, aux; char *result; index = strlen(pathname)-1; /* Chop off trailing seperators. */ while (index > 0 && (pathname[index]==FILESEPARATOR || pathname[index]==FILESEPARATOR2)) { --index; } aux = index; /* Now find the next separator. */ while (index >= 0 && pathname[index] != FILESEPARATOR && pathname[index] != FILESEPARATOR2) { --index; } /* Allocate and copy. */ result = JS_malloc(cx, aux - index + 1); if (!result) return NULL; strncpy(result, pathname + index + 1, aux - index); result[aux - index] = '\0'; return result; } /* * Returns everything but the last component from a path name. * Returned string must be freed. */ static char * js_fileDirectoryName(JSContext *cx, const char *pathname) { char *result; const char *cp, *end; size_t pathsize; end = pathname + strlen(pathname); cp = end - 1; /* If this is already a directory, chop off the trailing /s. */ while (cp >= pathname) { if (*cp != FILESEPARATOR && *cp != FILESEPARATOR2) break; --cp; } if (cp < pathname && end != pathname) { /* There were just /s, return the root. */ result = JS_malloc(cx, 1 + 1); /* The separator + trailing NUL. */ result[0] = FILESEPARATOR; result[1] = '\0'; return result; } /* Now chop off the last portion. */ while (cp >= pathname) { if (*cp == FILESEPARATOR || *cp == FILESEPARATOR2) break; --cp; } /* Check if this is a leaf. */ if (cp < pathname) { /* It is, return "pathname/". */ if (end[-1] == FILESEPARATOR || end[-1] == FILESEPARATOR2) { /* Already has its terminating /. */ return JS_strdup(cx, pathname); } pathsize = end - pathname + 1; result = JS_malloc(cx, pathsize + 1); if (!result) return NULL; strcpy(result, pathname); result[pathsize - 1] = FILESEPARATOR; result[pathsize] = '\0'; return result; } /* Return everything up to and including the seperator. */ pathsize = cp - pathname + 1; result = JS_malloc(cx, pathsize + 1); if (!result) return NULL; strncpy(result, pathname, pathsize); result[pathsize] = '\0'; return result; } static char * js_absolutePath(JSContext *cx, const char * path) { JSObject *obj; JSString *str; jsval prop; if (js_isAbsolute(path)) { return JS_strdup(cx, path); } else { obj = JS_GetGlobalObject(cx); if (!JS_GetProperty(cx, obj, FILE_CONSTRUCTOR, &prop)) { JS_ReportErrorNumber(cx, JSFile_GetErrorMessage, NULL, JSFILEMSG_FILE_CONSTRUCTOR_UNDEFINED_ERROR); return JS_strdup(cx, path); } obj = JSVAL_TO_OBJECT(prop); if (!JS_GetProperty(cx, obj, CURRENTDIR_PROPERTY, &prop)) { JS_ReportErrorNumber(cx, JSFile_GetErrorMessage, NULL, JSFILEMSG_FILE_CURRENTDIR_UNDEFINED_ERROR); return JS_strdup(cx, path); } str = JS_ValueToString(cx, prop); if (!str) return JS_strdup(cx, path); /* should we have an array of curr dirs indexed by drive for windows? */ return js_combinePath(cx, JS_GetStringBytes(str), path); } } /* Side effect: will remove spaces in the beginning/end of the filename */ static char * js_canonicalPath(JSContext *cx, char *oldpath) { char *tmp; char *path = oldpath; char *base, *dir, *current, *result; jsint c; jsint back = 0; unsigned int i = 0, j = strlen(path)-1; /* This is probably optional */ /* Remove possible spaces in the beginning and end */ while (i < j && path[i] == ' ') i++; while (j >= 0 && path[j] == ' ') j--; tmp = JS_malloc(cx, j-i+2); if (!tmp) return NULL; strncpy(tmp, path + i, j - i + 1); tmp[j - i + 1] = '\0'; path = tmp; /* Pipe support. */ if (js_filenameHasAPipe(path)) return path; /* file:// support. */ if (!strncmp(path, URL_PREFIX, strlen(URL_PREFIX))) { tmp = js_canonicalPath(cx, path + strlen(URL_PREFIX)); JS_free(cx, path); return tmp; } if (!js_isAbsolute(path)) { tmp = js_absolutePath(cx, path); if (!tmp) return NULL; path = tmp; } result = JS_strdup(cx, ""); current = path; base = js_fileBaseName(cx, current); dir = js_fileDirectoryName(cx, current); while (strcmp(dir, current)) { if (!strcmp(base, "..")) { back++; } else { if (back > 0) { back--; } else { tmp = result; result = JS_malloc(cx, strlen(base) + 1 + strlen(tmp) + 1); if (!result) goto out; strcpy(result, base); c = strlen(result); if (*tmp) { result[c] = FILESEPARATOR; result[c + 1] = '\0'; strcat(result, tmp); } JS_free(cx, tmp); } } JS_free(cx, current); JS_free(cx, base); current = dir; base = js_fileBaseName(cx, current); dir = js_fileDirectoryName(cx, current); } tmp = result; result = JS_malloc(cx, strlen(dir)+1+strlen(tmp)+1); if (!result) goto out; strcpy(result, dir); c = strlen(result); if (tmp[0]!='\0') { if ((result[c-1]!=FILESEPARATOR)&&(result[c-1]!=FILESEPARATOR2)) { result[c] = FILESEPARATOR; result[c+1] = '\0'; } strcat(result, tmp); } out: if (tmp) JS_free(cx, tmp); if (dir) JS_free(cx, dir); if (base) JS_free(cx, base); if (current) JS_free(cx, current); return result; } /* -------------------------- Text conversion ------------------------------- */ /* The following is ripped from libi18n/unicvt.c and include files.. */ /* * UTF8 defines and macros */ #define ONE_OCTET_BASE 0x00 /* 0xxxxxxx */ #define ONE_OCTET_MASK 0x7F /* x1111111 */ #define CONTINUING_OCTET_BASE 0x80 /* 10xxxxxx */ #define CONTINUING_OCTET_MASK 0x3F /* 00111111 */ #define TWO_OCTET_BASE 0xC0 /* 110xxxxx */ #define TWO_OCTET_MASK 0x1F /* 00011111 */ #define THREE_OCTET_BASE 0xE0 /* 1110xxxx */ #define THREE_OCTET_MASK 0x0F /* 00001111 */ #define FOUR_OCTET_BASE 0xF0 /* 11110xxx */ #define FOUR_OCTET_MASK 0x07 /* 00000111 */ #define FIVE_OCTET_BASE 0xF8 /* 111110xx */ #define FIVE_OCTET_MASK 0x03 /* 00000011 */ #define SIX_OCTET_BASE 0xFC /* 1111110x */ #define SIX_OCTET_MASK 0x01 /* 00000001 */ #define IS_UTF8_1ST_OF_1(x) (( (x)&~ONE_OCTET_MASK ) == ONE_OCTET_BASE) #define IS_UTF8_1ST_OF_2(x) (( (x)&~TWO_OCTET_MASK ) == TWO_OCTET_BASE) #define IS_UTF8_1ST_OF_3(x) (( (x)&~THREE_OCTET_MASK) == THREE_OCTET_BASE) #define IS_UTF8_1ST_OF_4(x) (( (x)&~FOUR_OCTET_MASK ) == FOUR_OCTET_BASE) #define IS_UTF8_1ST_OF_5(x) (( (x)&~FIVE_OCTET_MASK ) == FIVE_OCTET_BASE) #define IS_UTF8_1ST_OF_6(x) (( (x)&~SIX_OCTET_MASK ) == SIX_OCTET_BASE) #define IS_UTF8_2ND_THRU_6TH(x) \ (( (x)&~CONTINUING_OCTET_MASK ) == CONTINUING_OCTET_BASE) #define IS_UTF8_1ST_OF_UCS2(x) \ IS_UTF8_1ST_OF_1(x) \ || IS_UTF8_1ST_OF_2(x) \ || IS_UTF8_1ST_OF_3(x) #define MAX_UCS2 0xFFFF #define DEFAULT_CHAR 0x003F /* Default char is "?" */ #define BYTE_MASK 0xBF #define BYTE_MARK 0x80 /* Function: one_ucs2_to_utf8_char * * Function takes one UCS-2 char and writes it to a UTF-8 buffer. * We need a UTF-8 buffer because we don't know before this * function how many bytes of utf-8 data will be written. It also * takes a pointer to the end of the UTF-8 buffer so that we don't * overwrite data. This function returns the number of UTF-8 bytes * of data written, or -1 if the buffer would have been overrun. */ #define LINE_SEPARATOR 0x2028 #define PARAGRAPH_SEPARATOR 0x2029 static int16 one_ucs2_to_utf8_char(unsigned char *tobufp, unsigned char *tobufendp, uint16 onechar) { int16 numUTF8bytes = 0; if (onechar == LINE_SEPARATOR || onechar == PARAGRAPH_SEPARATOR) { strcpy((char*)tobufp, "\n"); return strlen((char*)tobufp); } if (onechar < 0x80) { numUTF8bytes = 1; } else if (onechar < 0x800) { numUTF8bytes = 2; } else { /* 0x800 >= onechar <= MAX_UCS2 */ numUTF8bytes = 3; } tobufp += numUTF8bytes; /* return error if we don't have space for the whole character */ if (tobufp > tobufendp) { return(-1); } switch(numUTF8bytes) { case 3: *--tobufp = (onechar | BYTE_MARK) & BYTE_MASK; onechar >>=6; *--tobufp = (onechar | BYTE_MARK) & BYTE_MASK; onechar >>=6; *--tobufp = onechar | THREE_OCTET_BASE; break; case 2: *--tobufp = (onechar | BYTE_MARK) & BYTE_MASK; onechar >>=6; *--tobufp = onechar | TWO_OCTET_BASE; break; case 1: *--tobufp = (unsigned char)onechar; break; } return numUTF8bytes; } /* * utf8_to_ucs2_char * * Convert a utf8 multibyte character to ucs2 * * inputs: pointer to utf8 character(s) * length of utf8 buffer ("read" length limit) * pointer to return ucs2 character * * outputs: number of bytes in the utf8 character * -1 if not a valid utf8 character sequence * -2 if the buffer is too short */ static int16 utf8_to_ucs2_char(const unsigned char *utf8p, int16 buflen, uint16 *ucs2p) { uint16 lead, cont1, cont2; /* * Check for minimum buffer length */ if ((buflen < 1) || (utf8p == NULL)) { return -2; } lead = (uint16) (*utf8p); /* * Check for a one octet sequence */ if (IS_UTF8_1ST_OF_1(lead)) { *ucs2p = lead & ONE_OCTET_MASK; return 1; } /* * Check for a two octet sequence */ if (IS_UTF8_1ST_OF_2(*utf8p)) { if (buflen < 2) return -2; cont1 = (uint16) *(utf8p+1); if (!IS_UTF8_2ND_THRU_6TH(cont1)) return -1; *ucs2p = (lead & TWO_OCTET_MASK) << 6; *ucs2p |= cont1 & CONTINUING_OCTET_MASK; return 2; } /* * Check for a three octet sequence */ else if (IS_UTF8_1ST_OF_3(lead)) { if (buflen < 3) return -2; cont1 = (uint16) *(utf8p+1); cont2 = (uint16) *(utf8p+2); if ( (!IS_UTF8_2ND_THRU_6TH(cont1)) || (!IS_UTF8_2ND_THRU_6TH(cont2))) return -1; *ucs2p = (lead & THREE_OCTET_MASK) << 12; *ucs2p |= (cont1 & CONTINUING_OCTET_MASK) << 6; *ucs2p |= cont2 & CONTINUING_OCTET_MASK; return 3; } else { /* not a valid utf8/ucs2 character */ return -1; } } /* ----------------------------- Helper functions --------------------------- */ /* Ripped off from lm_win.c .. */ /* where is strcasecmp?.. for now, it's case sensitive.. * * strcasecmp is in strings.h, but on windows it's called _stricmp... * will need to #ifdef this */ static int32 js_FileHasOption(JSContext *cx, const char *oldoptions, const char *name) { char *comma, *equal, *current; char *options = JS_strdup(cx, oldoptions); int32 found = 0; current = options; for (;;) { comma = strchr(current, ','); if (comma) *comma = '\0'; equal = strchr(current, '='); if (equal) *equal = '\0'; if (strcmp(current, name) == 0) { if (!equal || strcmp(equal + 1, "yes") == 0) found = 1; else found = atoi(equal + 1); } if (equal) *equal = '='; if (comma) *comma = ','; if (found || !comma) break; current = comma + 1; } JS_free(cx, options); return found; } /* empty the buffer */ static void js_ResetBuffers(JSFile * file) { file->charBufferUsed = JS_FALSE; file->nbBytesInBuf = 0; } /* Reset file attributes */ static void js_ResetAttributes(JSFile * file) { file->mode = file->type = 0; file->isOpen = JS_FALSE; file->handle = NULL; file->nativehandle = NULL; file->hasRandomAccess = JS_TRUE; /* Innocent until proven guilty. */ file->hasAutoflush = JS_FALSE; file->isNative = JS_FALSE; file->isPipe = JS_FALSE; js_ResetBuffers(file); } static JSBool js_FileOpen(JSContext *cx, JSObject *obj, JSFile *file, char *mode){ JSString *type, *mask; jsval v[2]; jsval rval; type = JS_InternString(cx, asciistring); mask = JS_NewStringCopyZ(cx, mode); v[0] = STRING_TO_JSVAL(mask); v[1] = STRING_TO_JSVAL(type); if (!file_open(cx, obj, 2, v, &rval)) return JS_FALSE; return JS_TRUE; } /* Buffered version of PR_Read. Used by js_FileRead */ static int32 js_BufferedRead(JSFile *f, unsigned char *buf, int32 len) { int32 count = 0; while (f->nbBytesInBuf>0&&len>0) { buf[0] = f->byteBuffer[0]; f->byteBuffer[0] = f->byteBuffer[1]; f->byteBuffer[1] = f->byteBuffer[2]; f->nbBytesInBuf--; len--; buf+=1; count++; } if (len > 0) { count += (!f->isNative) ? PR_Read(f->handle, buf, len) : fread(buf, 1, len, f->nativehandle); } return count; } static int32 js_FileRead(JSContext *cx, JSFile *file, jschar *buf, int32 len, int32 mode) { unsigned char *aux; int32 count = 0, i; jsint remainder; unsigned char utfbuf[3]; if (file->charBufferUsed) { buf[0] = file->charBuffer; buf++; len--; file->charBufferUsed = JS_FALSE; } switch (mode) { case ASCII: aux = (unsigned char*)JS_malloc(cx, len); if (!aux) return 0; count = js_BufferedRead(file, aux, len); if (count == -1) { JS_free(cx, aux); return 0; } for (i = 0; i < len; i++) buf[i] = (jschar)aux[i]; JS_free(cx, aux); break; case UTF8: remainder = 0; for (count = 0;count0) { file->byteBuffer[file->nbBytesInBuf] = utfbuf[0]; file->nbBytesInBuf++; utfbuf[0] = utfbuf[1]; utfbuf[1] = utfbuf[2]; remainder--; } break; case UCS2: count = js_BufferedRead(file, (unsigned char *)buf, len * 2) >> 1; if (count == -1) return 0; break; default: /* Not reached. */ JS_ASSERT(0); } if(count == -1) { JS_ReportErrorNumber(cx, JSFile_GetErrorMessage, NULL, JSFILEMSG_OP_FAILED, "read", file->path); } return count; } static int32 js_FileSeek(JSContext *cx, JSFile *file, int32 len, int32 mode) { int32 count = 0, i; jsint remainder; unsigned char utfbuf[3]; jschar tmp; switch (mode) { case ASCII: count = PR_Seek(file->handle, len, PR_SEEK_CUR); break; case UTF8: remainder = 0; for (count = 0;count0) { file->byteBuffer[file->nbBytesInBuf] = utfbuf[0]; file->nbBytesInBuf++; utfbuf[0] = utfbuf[1]; utfbuf[1] = utfbuf[2]; remainder--; } break; case UCS2: count = PR_Seek(file->handle, len*2, PR_SEEK_CUR)/2; break; default: /* Not reached. */ JS_ASSERT(0); } if(count == -1) { JS_ReportErrorNumber(cx, JSFile_GetErrorMessage, NULL, JSFILEMSG_OP_FAILED, "seek", file->path); } return count; } static int32 js_FileWrite(JSContext *cx, JSFile *file, jschar *buf, int32 len, int32 mode) { unsigned char *aux; int32 count = 0, i, j; unsigned char *utfbuf; switch (mode) { case ASCII: aux = (unsigned char*)JS_malloc(cx, len); if (!aux) return 0; for (i = 0; iisNative) ? PR_Write(file->handle, aux, len) : fwrite(aux, 1, len, file->nativehandle); if (count==-1) { JS_free(cx, aux); return 0; } JS_free(cx, aux); break; case UTF8: utfbuf = (unsigned char*)JS_malloc(cx, len*3); if (!utfbuf) return 0; i = 0; for (count = 0;countisNative) ? PR_Write(file->handle, utfbuf, i) : fwrite(utfbuf, 1, i, file->nativehandle); if (jisNative) ? PR_Write(file->handle, buf, len*2) >> 1 : fwrite(buf, 1, len*2, file->nativehandle) >> 1; if (count == -1) return 0; break; default: /* Not reached. */ JS_ASSERT(0); } if(count == -1) { JS_ReportErrorNumber(cx, JSFile_GetErrorMessage, NULL, JSFILEMSG_OP_FAILED, "write", file->path); } return count; } /* ----------------------------- Property checkers -------------------------- */ static JSBool js_exists(JSContext *cx, JSFile *file) { if (file->isNative) { /* It doesn't make sense for a pipe of stdstream. */ return JS_FALSE; } return PR_Access(file->path, PR_ACCESS_EXISTS) == PR_SUCCESS; } static JSBool js_canRead(JSContext *cx, JSFile *file) { if (!file->isNative) { if (file->isOpen && !(file->mode & PR_RDONLY)) return JS_FALSE; return PR_Access(file->path, PR_ACCESS_READ_OK) == PR_SUCCESS; } if (file->isPipe) { /* Is this pipe open for reading? */ return file->path[0] == PIPE_SYMBOL; } return !strcmp(file->path, STDINPUT_NAME); } static JSBool js_canWrite(JSContext *cx, JSFile *file) { if (!file->isNative) { if (file->isOpen && !(file->mode & PR_WRONLY)) return JS_FALSE; return PR_Access(file->path, PR_ACCESS_WRITE_OK) == PR_SUCCESS; } if(file->isPipe) { /* Is this pipe open for writing? */ return file->path[strlen(file->path)-1] == PIPE_SYMBOL; } return !strcmp(file->path, STDOUTPUT_NAME) || !strcmp(file->path, STDERROR_NAME); } static JSBool js_isFile(JSContext *cx, JSFile *file) { if (!file->isNative) { PRFileInfo info; if (file->isOpen ? PR_GetOpenFileInfo(file->handle, &info) : PR_GetFileInfo(file->path, &info) != PR_SUCCESS) { JS_ReportErrorNumber(cx, JSFile_GetErrorMessage, NULL, JSFILEMSG_CANNOT_ACCESS_FILE_STATUS, file->path); return JS_FALSE; } return info.type == PR_FILE_FILE; } /* This doesn't make sense for a pipe of stdstream. */ return JS_FALSE; } static JSBool js_isDirectory(JSContext *cx, JSFile *file) { if(!file->isNative){ PRFileInfo info; /* Hack needed to get get_property to work. */ if (!js_exists(cx, file)) return JS_FALSE; if (file->isOpen ? PR_GetOpenFileInfo(file->handle, &info) : PR_GetFileInfo(file->path, &info) != PR_SUCCESS) { JS_ReportErrorNumber(cx, JSFile_GetErrorMessage, NULL, JSFILEMSG_CANNOT_ACCESS_FILE_STATUS, file->path); return JS_FALSE; } return info.type == PR_FILE_DIRECTORY; } /* This doesn't make sense for a pipe of stdstream. */ return JS_FALSE; } static jsval js_size(JSContext *cx, JSFile *file) { PRFileInfo info; JSFILE_CHECK_NATIVE("size"); if (file->isOpen ? PR_GetOpenFileInfo(file->handle, &info) : PR_GetFileInfo(file->path, &info) != PR_SUCCESS) { JS_ReportErrorNumber(cx, JSFile_GetErrorMessage, NULL, JSFILEMSG_CANNOT_ACCESS_FILE_STATUS, file->path); return JSVAL_VOID; } return INT_TO_JSVAL(info.size); out: return JSVAL_VOID; } /* * Return the parent object */ static JSBool js_parent(JSContext *cx, JSFile *file, jsval *resultp) { char *str; /* Since we only care about pipes and native files, return NULL. */ if (file->isNative) { *resultp = JSVAL_VOID; return JS_TRUE; } str = js_fileDirectoryName(cx, file->path); if (!str) return JS_FALSE; /* If the directory is equal to the original path, we're at the root. */ if (!strcmp(file->path, str)) { *resultp = JSVAL_NULL; } else { JSObject *obj = js_NewFileObject(cx, str); if (!obj) { JS_free(cx, str); return JS_FALSE; } *resultp = OBJECT_TO_JSVAL(obj); } JS_free(cx, str); return JS_TRUE; } static JSBool js_name(JSContext *cx, JSFile *file, jsval *vp) { char *name; JSString *str; if (file->isPipe) { *vp = JSVAL_VOID; return JS_TRUE; } name = js_fileBaseName(cx, file->path); if (!name) return JS_FALSE; str = JS_NewString(cx, name, strlen(name)); if (!str) { JS_free(cx, name); return JS_FALSE; } *vp = STRING_TO_JSVAL(str); return JS_TRUE; } /* ------------------------------ File object methods ---------------------------- */ static JSBool file_open(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) { JSFile *file = JS_GetInstancePrivate(cx, obj, &js_FileClass, NULL); JSString *strmode, *strtype; char *ctype, *mode; int32 mask, type; int len; mode = NULL; SECURITY_CHECK(cx, NULL, "open", file); /* A native file that is already open */ if(file->isOpen && file->isNative) { JS_ReportWarning(cx, "Native file %s is already open, proceeding", file->path); goto good; } /* Close before proceeding */ if (file->isOpen) { JS_ReportWarning(cx, "File %s is already open, we will close it and " "reopen, proceeding", file->path); if(!file_close(cx, obj, 0, NULL, rval)) goto out; } if (js_isDirectory(cx, file)) { JS_ReportWarning(cx, "%s seems to be a directory, there is no point in " "trying to open it, proceeding", file->path); goto good; } /* Path must be defined at this point */ len = strlen(file->path); /* Mode */ if (argc >= 1) { strmode = JS_ValueToString(cx, argv[0]); if (!strmode) { JS_ReportErrorNumber(cx, JSFile_GetErrorMessage, NULL, JSFILEMSG_FIRST_ARGUMENT_OPEN_NOT_STRING_ERROR, argv[0]); goto out; } mode = JS_strdup(cx, JS_GetStringBytes(strmode)); } else { if(file->path[0]==PIPE_SYMBOL) { /* pipe default mode */ mode = JS_strdup(cx, "read"); } else if(file->path[len-1]==PIPE_SYMBOL) { /* pipe default mode */ mode = JS_strdup(cx, "write"); } else { /* non-destructive, permissive defaults. */ mode = JS_strdup(cx, "readWrite,append,create"); } } /* Process the mode */ mask = 0; /* TODO: this is pretty ugly, we walk thru the string too many times */ mask |= js_FileHasOption(cx, mode, "read") ? PR_RDONLY : 0; mask |= js_FileHasOption(cx, mode, "write") ? PR_WRONLY : 0; mask |= js_FileHasOption(cx, mode, "readWrite")? PR_RDWR : 0; mask |= js_FileHasOption(cx, mode, "append") ? PR_APPEND : 0; mask |= js_FileHasOption(cx, mode, "create") ? PR_CREATE_FILE : 0; mask |= js_FileHasOption(cx, mode, "replace") ? PR_TRUNCATE : 0; if (mask & PR_RDWR) mask |= (PR_RDONLY | PR_WRONLY); if ((mask & PR_RDONLY) && (mask & PR_WRONLY)) mask |= PR_RDWR; file->hasAutoflush |= js_FileHasOption(cx, mode, "autoflush"); /* Type */ if (argc > 1) { strtype = JS_ValueToString(cx, argv[1]); if (!strtype) { JS_ReportErrorNumber(cx, JSFile_GetErrorMessage, NULL, JSFILEMSG_SECOND_ARGUMENT_OPEN_NOT_STRING_ERROR, argv[1]); goto out; } ctype = JS_GetStringBytes(strtype); if(!strcmp(ctype, utfstring)) { type = UTF8; } else if (!strcmp(ctype, unicodestring)) { type = UCS2; } else { if (strcmp(ctype, asciistring)) { JS_ReportWarning(cx, "File type %s is not supported, using " "'text' instead, proceeding", ctype); } type = ASCII; } } else { type = ASCII; } /* Save the relevant fields */ file->type = type; file->mode = mask; file->nativehandle = NULL; file->hasRandomAccess = (type != UTF8); /* * Deal with pipes here. We can't use NSPR for pipes, so we have to use * POPEN. */ if (file->path[0]==PIPE_SYMBOL || file->path[len-1]==PIPE_SYMBOL) { if (file->path[0] == PIPE_SYMBOL && file->path[len-1] == PIPE_SYMBOL) { JS_ReportErrorNumber(cx, JSFile_GetErrorMessage, NULL, JSFILEMSG_BIDIRECTIONAL_PIPE_NOT_SUPPORTED); goto out; } else { int i = 0; char pipemode[3]; SECURITY_CHECK(cx, NULL, "pipe_open", file); if(file->path[0] == PIPE_SYMBOL){ if(mask & (PR_WRONLY | PR_APPEND | PR_CREATE_FILE | PR_TRUNCATE)){ JS_ReportErrorNumber(cx, JSFile_GetErrorMessage, NULL, JSFILEMSG_OPEN_MODE_NOT_SUPPORTED_WITH_PIPES, mode, file->path); goto out; } /* open(SPOOLER, "| cat -v | lpr -h 2>/dev/null") -- pipe for writing */ pipemode[i++] = 'r'; #ifndef XP_UNIX pipemode[i++] = file->type==UTF8 ? 'b' : 't'; #endif pipemode[i++] = '\0'; file->nativehandle = POPEN(&file->path[1], pipemode); } else if(file->path[len-1] == PIPE_SYMBOL) { char *command = JS_malloc(cx, len); strncpy(command, file->path, len-1); command[len-1] = '\0'; /* open(STATUS, "netstat -an 2>&1 |") */ pipemode[i++] = 'w'; #ifndef XP_UNIX pipemode[i++] = file->type==UTF8 ? 'b' : 't'; #endif pipemode[i++] = '\0'; file->nativehandle = POPEN(command, pipemode); JS_free(cx, command); } /* set the flags */ file->isNative = JS_TRUE; file->isPipe = JS_TRUE; file->hasRandomAccess = JS_FALSE; } } else { /* TODO: what about the permissions?? Java ignores the problem... */ file->handle = PR_Open(file->path, mask, 0644); } js_ResetBuffers(file); JS_free(cx, mode); mode = NULL; /* Set the open flag and return result */ if (file->handle == NULL && file->nativehandle == NULL) { file->isOpen = JS_FALSE; JS_ReportErrorNumber(cx, JSFile_GetErrorMessage, NULL, JSFILEMSG_OP_FAILED, "open", file->path); goto out; } good: file->isOpen = JS_TRUE; *rval = JSVAL_TRUE; return JS_TRUE; out: if(mode) JS_free(cx, mode); return JS_FALSE; } static JSBool file_close(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) { JSFile *file = JS_GetInstancePrivate(cx, obj, &js_FileClass, NULL); SECURITY_CHECK(cx, NULL, "close", file); if(!file->isOpen){ JS_ReportWarning(cx, "File %s is not open, can't close it, proceeding", file->path); goto out; } if(!file->isPipe){ if(file->isNative){ JS_ReportWarning(cx, "Unable to close a native file, proceeding", file->path); goto out; }else{ if(file->handle && PR_Close(file->handle)){ JS_ReportErrorNumber(cx, JSFile_GetErrorMessage, NULL, JSFILEMSG_OP_FAILED, "close", file->path); goto out; } } }else{ if(PCLOSE(file->nativehandle)==-1){ JS_ReportErrorNumber(cx, JSFile_GetErrorMessage, NULL, JSFILEMSG_OP_FAILED, "pclose", file->path); goto out; } } js_ResetAttributes(file); *rval = JSVAL_TRUE; return JS_TRUE; out: return JS_FALSE; } static JSBool file_remove(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) { JSFile *file = JS_GetInstancePrivate(cx, obj, &js_FileClass, NULL); SECURITY_CHECK(cx, NULL, "remove", file); JSFILE_CHECK_NATIVE("remove"); JSFILE_CHECK_CLOSED("remove"); if ((js_isDirectory(cx, file) ? PR_RmDir(file->path) : PR_Delete(file->path))==PR_SUCCESS) { js_ResetAttributes(file); *rval = JSVAL_TRUE; return JS_TRUE; } else { JS_ReportErrorNumber(cx, JSFile_GetErrorMessage, NULL, JSFILEMSG_OP_FAILED, "remove", file->path); goto out; } out: *rval = JSVAL_FALSE; return JS_FALSE; } /* Raw PR-based function. No text processing. Just raw data copying. */ static JSBool file_copyTo(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) { JSFile *file = JS_GetInstancePrivate(cx, obj, &js_FileClass, NULL); char *dest = NULL; PRFileDesc *handle = NULL; char *buffer; jsval count, size; JSBool fileInitiallyOpen=JS_FALSE; SECURITY_CHECK(cx, NULL, "copyTo", file); /* may need a second argument!*/ JSFILE_CHECK_ONE_ARG("copyTo"); JSFILE_CHECK_NATIVE("copyTo"); /* remeber the state */ fileInitiallyOpen = file->isOpen; JSFILE_CHECK_READ; dest = JS_GetStringBytes(JS_ValueToString(cx, argv[0])); /* make sure we are not reading a file open for writing */ if (file->isOpen && !js_canRead(cx, file)) { JS_ReportErrorNumber(cx, JSFile_GetErrorMessage, NULL, JSFILEMSG_CANNOT_COPY_FILE_OPEN_FOR_WRITING_ERROR, file->path); goto out; } if (file->handle==NULL){ JS_ReportErrorNumber(cx, JSFile_GetErrorMessage, NULL, JSFILEMSG_OP_FAILED, "open", file->path); goto out; } handle = PR_Open(dest, PR_WRONLY|PR_CREATE_FILE|PR_TRUNCATE, 0644); if(!handle){ JS_ReportErrorNumber(cx, JSFile_GetErrorMessage, NULL, JSFILEMSG_OP_FAILED, "open", dest); goto out; } if ((size=js_size(cx, file))==JSVAL_VOID) { goto out; } buffer = JS_malloc(cx, size); count = INT_TO_JSVAL(PR_Read(file->handle, buffer, size)); /* reading panic */ if (count!=size) { JS_free(cx, buffer); JS_ReportErrorNumber(cx, JSFile_GetErrorMessage, NULL, JSFILEMSG_COPY_READ_ERROR, file->path); goto out; } count = INT_TO_JSVAL(PR_Write(handle, buffer, JSVAL_TO_INT(size))); /* writing panic */ if (count!=size) { JS_free(cx, buffer); JS_ReportErrorNumber(cx, JSFile_GetErrorMessage, NULL, JSFILEMSG_COPY_WRITE_ERROR, file->path); goto out; } JS_free(cx, buffer); if(!fileInitiallyOpen){ if(!file_close(cx, obj, 0, NULL, rval)) goto out; } if(PR_Close(handle)!=PR_SUCCESS){ JS_ReportErrorNumber(cx, JSFile_GetErrorMessage, NULL, JSFILEMSG_OP_FAILED, "close", dest); goto out; } *rval = JSVAL_TRUE; return JS_TRUE; out: if(file->isOpen && !fileInitiallyOpen){ if(PR_Close(file->handle)!=PR_SUCCESS){ JS_ReportWarning(cx, "Can't close %s, proceeding", file->path); } } if(handle && PR_Close(handle)!=PR_SUCCESS){ JS_ReportWarning(cx, "Can't close %s, proceeding", dest); } *rval = JSVAL_FALSE; return JS_FALSE; } static JSBool file_renameTo(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) { JSFile *file = JS_GetInstancePrivate(cx, obj, &js_FileClass, NULL); char *dest; SECURITY_CHECK(cx, NULL, "renameTo", file); /* may need a second argument!*/ JSFILE_CHECK_ONE_ARG("renameTo"); JSFILE_CHECK_NATIVE("renameTo"); JSFILE_CHECK_CLOSED("renameTo"); dest = RESOLVE_PATH(cx, JS_GetStringBytes(JS_ValueToString(cx, argv[0]))); if (PR_Rename(file->path, dest)==PR_SUCCESS){ /* copy the new filename */ JS_free(cx, file->path); file->path = dest; *rval = JSVAL_TRUE; return JS_TRUE; }else{ JS_ReportErrorNumber(cx, JSFile_GetErrorMessage, NULL, JSFILEMSG_RENAME_FAILED, file->path, dest); goto out; } out: *rval = JSVAL_FALSE; return JS_FALSE; } static JSBool file_flush(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) { JSFile *file = JS_GetInstancePrivate(cx, obj, &js_FileClass, NULL); SECURITY_CHECK(cx, NULL, "flush", file); JSFILE_CHECK_NATIVE("flush"); JSFILE_CHECK_OPEN("flush"); if (PR_Sync(file->handle)==PR_SUCCESS){ *rval = JSVAL_TRUE; return JS_TRUE; }else{ JS_ReportErrorNumber(cx, JSFile_GetErrorMessage, NULL, JSFILEMSG_OP_FAILED, "flush", file->path); goto out; } out: *rval = JSVAL_FALSE; return JS_FALSE; } static JSBool file_write(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) { JSFile *file = JS_GetInstancePrivate(cx, obj, &js_FileClass, NULL); JSString *str; int32 count; uintN i; SECURITY_CHECK(cx, NULL, "write", file); JSFILE_CHECK_WRITE; for (i = 0; itype); if (count==-1){ *rval = JSVAL_FALSE; return JS_FALSE; } } *rval = JSVAL_TRUE; return JS_TRUE; out: *rval = JSVAL_FALSE; return JS_FALSE; } static JSBool file_writeln(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) { JSFile *file = JS_GetInstancePrivate(cx, obj, &js_FileClass, NULL); JSString *str; SECURITY_CHECK(cx, NULL, "writeln", file); JSFILE_CHECK_WRITE; /* don't report an error here */ if(!file_write(cx, obj, argc, argv, rval)) return JS_FALSE; /* don't do security here -- we passed the check in file_write */ str = JS_NewStringCopyZ(cx, "\n"); if (js_FileWrite(cx, file, JS_GetStringChars(str), JS_GetStringLength(str), file->type)==-1){ *rval = JSVAL_FALSE; return JS_FALSE; } /* eol causes flush if hasAutoflush is turned on */ if (file->hasAutoflush) file_flush(cx, obj, 0, NULL, rval); *rval = JSVAL_TRUE; return JS_TRUE; out: *rval = JSVAL_FALSE; return JS_FALSE; } static JSBool file_writeAll(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) { JSFile *file = JS_GetInstancePrivate(cx, obj, &js_FileClass, NULL); jsuint i; jsuint limit; JSObject *array; JSObject *elem; jsval elemval; SECURITY_CHECK(cx, NULL, "writeAll", file); JSFILE_CHECK_ONE_ARG("writeAll"); JSFILE_CHECK_WRITE; if (!JS_IsArrayObject(cx, JSVAL_TO_OBJECT(argv[0]))) { JS_ReportErrorNumber(cx, JSFile_GetErrorMessage, NULL, JSFILEMSG_FIRST_ARGUMENT_WRITEALL_NOT_ARRAY_ERROR); goto out; } array = JSVAL_TO_OBJECT(argv[0]); JS_GetArrayLength(cx, array, &limit); for (i = 0; i262144)?262144:want; * arbitrary size limitation */ buf = JS_malloc(cx, want*sizeof buf[0]); if (!buf) goto out; count = js_FileRead(cx, file, buf, want, file->type); if (count>0) { str = JS_NewUCStringCopyN(cx, buf, count); *rval = STRING_TO_JSVAL(str); JS_free(cx, buf); return JS_TRUE; } else { JS_free(cx, buf); goto out; } out: *rval = JSVAL_FALSE; return JS_FALSE; } static JSBool file_readln(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) { JSFile *file = JS_GetInstancePrivate(cx, obj, &js_FileClass, NULL); JSString *str; jschar *buf = NULL, *tmp; int32 offset, read; intN room; jschar data, data2; SECURITY_CHECK(cx, NULL, "readln", file); JSFILE_CHECK_READ; buf = JS_malloc(cx, MAX_LINE_LENGTH * sizeof data); if (!buf) return JS_FALSE; room = MAX_LINE_LENGTH - 1; offset = 0; for (;;) { read = js_FileRead(cx, file, &data, 1, file->type); if (read < 0) goto out; if (read == 0) goto eof; switch (data) { case '\r': read = js_FileRead(cx, file, &data2, 1, file->type); if (read < 0) goto out; if (read == 1 && data2 != '\n') { /* We read one char too far. Buffer it. */ file->charBuffer = data2; file->charBufferUsed = JS_TRUE; } /* Fall through. */ case '\n': goto done; default: if (--room < 0) { tmp = JS_realloc(cx, buf, (offset + MAX_LINE_LENGTH) * sizeof data); if (!tmp) goto out; room = MAX_LINE_LENGTH - 1; buf = tmp; } buf[offset++] = data; break; } } eof: if (offset == 0) { *rval = JSVAL_NULL; return JS_TRUE; } done: buf[offset] = 0; tmp = JS_realloc(cx, buf, (offset + 1) * sizeof data); if (!tmp) goto out; str = JS_NewUCString(cx, tmp, offset); if (!str) goto out; *rval = STRING_TO_JSVAL(str); return JS_TRUE; out: if (buf) JS_free(cx, buf); return JS_FALSE; } static JSBool file_readAll(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) { JSFile *file = JS_GetInstancePrivate(cx, obj, &js_FileClass, NULL); JSObject *array; jsint len; jsval line; JSBool lineok = JS_FALSE; SECURITY_CHECK(cx, NULL, "readAll", file); JSFILE_CHECK_READ; array = JS_NewArrayObject(cx, 0, NULL); if (!array) return JS_FALSE; *rval = OBJECT_TO_JSVAL(array); len = 0; lineok = file_readln(cx, obj, 0, NULL, &line); while (lineok && !JSVAL_IS_NULL(line)) { JS_SetElement(cx, array, len++, &line); lineok = file_readln(cx, obj, 0, NULL, &line); } out: return lineok; } static JSBool file_seek(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) { JSFile *file = JS_GetInstancePrivate(cx, obj, &js_FileClass, NULL); int32 toskip; int32 pos; SECURITY_CHECK(cx, NULL, "seek", file); JSFILE_CHECK_ONE_ARG("seek"); JSFILE_CHECK_NATIVE("seek"); JSFILE_CHECK_READ; if (!JS_ValueToInt32(cx, argv[0], &toskip)){ JS_ReportErrorNumber(cx, JSFile_GetErrorMessage, NULL, JSFILEMSG_FIRST_ARGUMENT_MUST_BE_A_NUMBER, "seek", argv[0]); goto out; } if(!file->hasRandomAccess){ JS_ReportErrorNumber(cx, JSFile_GetErrorMessage, NULL, JSFILEMSG_NO_RANDOM_ACCESS, file->path); goto out; } if(js_isDirectory(cx, file)){ JS_ReportWarning(cx,"Seek on directories is not supported, proceeding"); goto out; } pos = js_FileSeek(cx, file, toskip, file->type); if (pos!=-1) { *rval = INT_TO_JSVAL(pos); return JS_TRUE; } out: *rval = JSVAL_VOID; return JS_FALSE; } static JSBool file_list(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) { PRDir *dir; PRDirEntry *entry; JSFile *file = JS_GetInstancePrivate(cx, obj, &js_FileClass, NULL); JSObject *array; JSObject *eachFile; jsint len; jsval v; JSRegExp *re = NULL; JSFunction *func = NULL; JSString *str; jsval args[1]; char *filePath; SECURITY_CHECK(cx, NULL, "list", file); JSFILE_CHECK_NATIVE("list"); if (argc==1) { if (JSVAL_IS_REGEXP(cx, argv[0])) { re = JS_GetPrivate(cx, JSVAL_TO_OBJECT(argv[0])); }else if (VALUE_IS_FUNCTION(cx, argv[0])) { func = JS_GetPrivate(cx, JSVAL_TO_OBJECT(argv[0])); }else{ JS_ReportErrorNumber(cx, JSFile_GetErrorMessage, NULL, JSFILEMSG_FIRST_ARGUMENT_MUST_BE_A_FUNCTION_OR_REGEX, argv[0]); goto out; } } if (!js_isDirectory(cx, file)) { JS_ReportErrorNumber(cx, JSFile_GetErrorMessage, NULL, JSFILEMSG_CANNOT_DO_LIST_ON_A_FILE, file->path); goto out; } dir = PR_OpenDir(file->path); if(!dir){ JS_ReportErrorNumber(cx, JSFile_GetErrorMessage, NULL, JSFILEMSG_OP_FAILED, "open", file->path); goto out; } /* create JSArray here... */ array = JS_NewArrayObject(cx, 0, NULL); len = 0; while ((entry = PR_ReadDir(dir, PR_SKIP_BOTH))!=NULL) { /* first, check if we have a regexp */ if (re!=NULL) { size_t index = 0; str = JS_NewStringCopyZ(cx, entry->name); if(!js_ExecuteRegExp(cx, re, str, &index, JS_TRUE, &v)){ /* don't report anything here */ goto out; } /* not matched! */ if (JSVAL_IS_NULL(v)) { continue; } }else if (func!=NULL) { str = JS_NewStringCopyZ(cx, entry->name); args[0] = STRING_TO_JSVAL(str); if(!JS_CallFunction(cx, obj, func, 1, args, &v)){ goto out; } if (v==JSVAL_FALSE) { continue; } } filePath = js_combinePath(cx, file->path, (char*)entry->name); eachFile = js_NewFileObject(cx, filePath); JS_free(cx, filePath); if (!eachFile){ JS_ReportWarning(cx, "File %s cannot be retrieved", filePath); continue; } v = OBJECT_TO_JSVAL(eachFile); JS_SetElement(cx, array, len, &v); JS_SetProperty(cx, array, entry->name, &v); len++; } if(PR_CloseDir(dir)!=PR_SUCCESS){ JS_ReportErrorNumber(cx, JSFile_GetErrorMessage, NULL, JSFILEMSG_OP_FAILED, "close", file->path); goto out; } *rval = OBJECT_TO_JSVAL(array); return JS_TRUE; out: *rval = JSVAL_NULL; return JS_FALSE; } static JSBool file_mkdir(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) { JSFile *file = JS_GetInstancePrivate(cx, obj, &js_FileClass, NULL); SECURITY_CHECK(cx, NULL, "mkdir", file); JSFILE_CHECK_ONE_ARG("mkdir"); JSFILE_CHECK_NATIVE("mkdir"); /* if the current file is not a directory, find out the directory name */ if (!js_isDirectory(cx, file)) { char *dir = js_fileDirectoryName(cx, file->path); JSObject *dirObj = js_NewFileObject(cx, dir); JS_free(cx, dir); /* call file_mkdir with the right set of parameters if needed */ if (file_mkdir(cx, dirObj, argc, argv, rval)) return JS_TRUE; else goto out; }else{ char *dirName = JS_GetStringBytes(JS_ValueToString(cx, argv[0])); char *fullName; fullName = js_combinePath(cx, file->path, dirName); if (PR_MkDir(fullName, 0755)==PR_SUCCESS){ *rval = JSVAL_TRUE; JS_free(cx, fullName); return JS_TRUE; }else{ JS_ReportErrorNumber(cx, JSFile_GetErrorMessage, NULL, JSFILEMSG_OP_FAILED, "mkdir", fullName); JS_free(cx, fullName); goto out; } } out: *rval = JSVAL_FALSE; return JS_FALSE; } static JSBool file_toString(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval*rval) { JSFile *file = JS_GetInstancePrivate(cx, obj, &js_FileClass, NULL); JSString *str; str = JS_NewStringCopyZ(cx, file->path); if (!str) return JS_FALSE; *rval = STRING_TO_JSVAL(str); return JS_TRUE; } static JSBool file_toURL(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) { JSFile *file = JS_GetInstancePrivate(cx, obj, &js_FileClass, NULL); char url[MAX_PATH_LENGTH]; jschar *urlChars; size_t len; JSString *str; JSFILE_CHECK_NATIVE("toURL"); sprintf(url, "file://%s", file->path); len = strlen(url); urlChars = js_InflateString(cx, url, &len); if (!urlChars) return JS_FALSE; str = js_NewString(cx, urlChars, len, 0); if (!str) { JS_free(cx, urlChars); return JS_FALSE; } *rval = STRING_TO_JSVAL(str); /* TODO: js_escape in jsstr.h may go away at some point */ return js_str_escape(cx, obj, 0, rval, rval); out: *rval = JSVAL_VOID; return JS_FALSE; } static void file_finalize(JSContext *cx, JSObject *obj) { JSFile *file = JS_GetInstancePrivate(cx, obj, &js_FileClass, NULL); if(file) { /* Close the file before exiting. */ if(file->isOpen && !file->isNative) { jsval vp; file_close(cx, obj, 0, NULL, &vp); } if (file->path) JS_free(cx, file->path); JS_free(cx, file); } } /* Allocates memory for the file object, sets fields to defaults. */ static JSFile* file_init(JSContext *cx, JSObject *obj, char *bytes) { JSFile *file; file = JS_malloc(cx, sizeof *file); if (!file) return NULL; memset(file, 0 , sizeof *file); js_ResetAttributes(file); file->path = RESOLVE_PATH(cx, bytes); if (!JS_SetPrivate(cx, obj, file)) { JS_ReportErrorNumber(cx, JSFile_GetErrorMessage, NULL, JSFILEMSG_CANNOT_SET_PRIVATE_FILE, file->path); JS_free(cx, file); return NULL; } return file; } /* Returns a JSObject. This function is globally visible */ JS_PUBLIC_API(JSObject*) js_NewFileObject(JSContext *cx, char *filename) { JSObject *obj; JSFile *file; obj = JS_NewObject(cx, &js_FileClass, NULL, NULL); if (!obj){ JS_ReportErrorNumber(cx, JSFile_GetErrorMessage, NULL, JSFILEMSG_OBJECT_CREATION_FAILED, "js_NewFileObject"); return NULL; } file = file_init(cx, obj, filename); if(!file) return NULL; return obj; } /* Internal function, used for cases which NSPR file support doesn't cover */ JSObject* js_NewFileObjectFromFILE(JSContext *cx, FILE *nativehandle, char *filename, int32 mode, JSBool open, JSBool randomAccess) { JSObject *obj; JSFile *file; obj = JS_NewObject(cx, &js_FileClass, NULL, NULL); if (!obj){ JS_ReportErrorNumber(cx, JSFile_GetErrorMessage, NULL, JSFILEMSG_OBJECT_CREATION_FAILED, "js_NewFileObjectFromFILE"); return NULL; } file = file_init(cx, obj, filename); if(!file) return NULL; file->nativehandle = nativehandle; /* free result of RESOLVE_PATH from file_init. */ JS_ASSERT(file->path != NULL); JS_free(cx, file->path); file->path = strdup(filename); file->isOpen = open; file->mode = mode; file->hasRandomAccess = randomAccess; file->isNative = JS_TRUE; return obj; } /* Real file constructor that is called from JavaScript. Basically, does error processing and calls file_init. */ static JSBool file_constructor(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) { JSString *str; JSFile *file; if (!(cx->fp->flags & JSFRAME_CONSTRUCTING)) { /* Replace obj with a new File object. */ obj = JS_NewObject(cx, &js_FileClass, NULL, NULL); if (!obj) return JS_FALSE; *rval = OBJECT_TO_JSVAL(obj); } str = (argc == 0) ? JS_InternString(cx, "") : JS_ValueToString(cx, argv[0]); if (!str) { JS_ReportErrorNumber(cx, JSFile_GetErrorMessage, NULL, JSFILEMSG_FIRST_ARGUMENT_CONSTRUCTOR_NOT_STRING_ERROR, argv[0]); return JS_FALSE; } file = file_init(cx, obj, JS_GetStringBytes(str)); if (!file) return JS_FALSE; SECURITY_CHECK(cx, NULL, "constructor", file); return JS_TRUE; } /* -------------------- File methods and properties ------------------------- */ static JSFunctionSpec file_functions[] = { { "open", file_open, 0}, { "close", file_close, 0}, { "remove", file_remove, 0}, { "copyTo", file_copyTo, 0}, { "renameTo", file_renameTo, 0}, { "flush", file_flush, 0}, { "seek", file_seek, 0}, { "read", file_read, 0}, { "readln", file_readln, 0}, { "readAll", file_readAll, 0}, { "write", file_write, 0}, { "writeln", file_writeln, 0}, { "writeAll", file_writeAll, 0}, { "list", file_list, 0}, { "mkdir", file_mkdir, 0}, { "toString", file_toString, 0}, { "toURL", file_toURL, 0}, {0} }; enum file_tinyid { FILE_LENGTH = -2, FILE_PARENT = -3, FILE_PATH = -4, FILE_NAME = -5, FILE_ISDIR = -6, FILE_ISFILE = -7, FILE_EXISTS = -8, FILE_CANREAD = -9, FILE_CANWRITE = -10, FILE_OPEN = -11, FILE_TYPE = -12, FILE_MODE = -13, FILE_CREATED = -14, FILE_MODIFIED = -15, FILE_SIZE = -16, FILE_RANDOMACCESS = -17, FILE_POSITION = -18, FILE_APPEND = -19, FILE_REPLACE = -20, FILE_AUTOFLUSH = -21, FILE_ISNATIVE = -22, }; static JSPropertySpec file_props[] = { {"length", FILE_LENGTH, JSPROP_ENUMERATE | JSPROP_READONLY }, {"parent", FILE_PARENT, JSPROP_ENUMERATE | JSPROP_READONLY }, {"path", FILE_PATH, JSPROP_ENUMERATE | JSPROP_READONLY }, {"name", FILE_NAME, JSPROP_ENUMERATE | JSPROP_READONLY }, {"isDirectory", FILE_ISDIR, JSPROP_ENUMERATE | JSPROP_READONLY }, {"isFile", FILE_ISFILE, JSPROP_ENUMERATE | JSPROP_READONLY }, {"exists", FILE_EXISTS, JSPROP_ENUMERATE | JSPROP_READONLY }, {"canRead", FILE_CANREAD, JSPROP_ENUMERATE | JSPROP_READONLY }, {"canWrite", FILE_CANWRITE, JSPROP_ENUMERATE | JSPROP_READONLY }, {"canAppend", FILE_APPEND, JSPROP_ENUMERATE | JSPROP_READONLY }, {"canReplace", FILE_REPLACE, JSPROP_ENUMERATE | JSPROP_READONLY }, {"isOpen", FILE_OPEN, JSPROP_ENUMERATE | JSPROP_READONLY }, {"type", FILE_TYPE, JSPROP_ENUMERATE | JSPROP_READONLY }, {"mode", FILE_MODE, JSPROP_ENUMERATE | JSPROP_READONLY }, {"creationTime", FILE_CREATED, JSPROP_ENUMERATE | JSPROP_READONLY }, {"lastModified", FILE_MODIFIED, JSPROP_ENUMERATE | JSPROP_READONLY }, {"size", FILE_SIZE, JSPROP_ENUMERATE | JSPROP_READONLY }, {"hasRandomAccess", FILE_RANDOMACCESS, JSPROP_ENUMERATE | JSPROP_READONLY }, {"hasAutoFlush", FILE_AUTOFLUSH, JSPROP_ENUMERATE | JSPROP_READONLY }, {"position", FILE_POSITION, JSPROP_ENUMERATE }, {"isNative", FILE_ISNATIVE, JSPROP_ENUMERATE | JSPROP_READONLY }, {0} }; /* ------------------------- Property getter/setter ------------------------- */ static JSBool file_getProperty(JSContext *cx, JSObject *obj, jsval id, jsval *vp) { JSFile *file = JS_GetInstancePrivate(cx, obj, &js_FileClass, NULL); char *bytes; JSString *str; jsint tiny; PRFileInfo info; JSBool flag; PRExplodedTime expandedTime; tiny = JSVAL_TO_INT(id); if (!file) return JS_TRUE; switch (tiny) { case FILE_PARENT: SECURITY_CHECK(cx, NULL, "parent", file); if (!js_parent(cx, file, vp)) return JS_FALSE; break; case FILE_PATH: str = JS_NewStringCopyZ(cx, file->path); if (!str) return JS_FALSE; *vp = STRING_TO_JSVAL(str); break; case FILE_NAME: if (!js_name(cx, file, vp)) return JS_FALSE; break; case FILE_ISDIR: SECURITY_CHECK(cx, NULL, "isDirectory", file); *vp = BOOLEAN_TO_JSVAL(js_isDirectory(cx, file)); break; case FILE_ISFILE: SECURITY_CHECK(cx, NULL, "isFile", file); *vp = BOOLEAN_TO_JSVAL(js_isFile(cx, file)); break; case FILE_EXISTS: SECURITY_CHECK(cx, NULL, "exists", file); *vp = BOOLEAN_TO_JSVAL(js_exists(cx, file)); break; case FILE_ISNATIVE: SECURITY_CHECK(cx, NULL, "isNative", file); *vp = BOOLEAN_TO_JSVAL(file->isNative); break; case FILE_CANREAD: SECURITY_CHECK(cx, NULL, "canRead", file); *vp = BOOLEAN_TO_JSVAL(js_canRead(cx, file)); break; case FILE_CANWRITE: SECURITY_CHECK(cx, NULL, "canWrite", file); *vp = BOOLEAN_TO_JSVAL(js_canWrite(cx, file)); break; case FILE_OPEN: SECURITY_CHECK(cx, NULL, "isOpen", file); *vp = BOOLEAN_TO_JSVAL(file->isOpen); break; case FILE_APPEND : SECURITY_CHECK(cx, NULL, "canAppend", file); JSFILE_CHECK_OPEN("canAppend"); *vp = BOOLEAN_TO_JSVAL(!file->isNative && (file->mode&PR_APPEND)==PR_APPEND); break; case FILE_REPLACE : SECURITY_CHECK(cx, NULL, "canReplace", file); JSFILE_CHECK_OPEN("canReplace"); *vp = BOOLEAN_TO_JSVAL(!file->isNative && (file->mode&PR_TRUNCATE)==PR_TRUNCATE); break; case FILE_AUTOFLUSH : SECURITY_CHECK(cx, NULL, "hasAutoFlush", file); JSFILE_CHECK_OPEN("hasAutoFlush"); *vp = BOOLEAN_TO_JSVAL(!file->isNative && file->hasAutoflush); break; case FILE_TYPE: SECURITY_CHECK(cx, NULL, "type", file); JSFILE_CHECK_OPEN("type"); if(js_isDirectory(cx, file)){ *vp = JSVAL_VOID; break; } switch (file->type) { case ASCII: *vp = STRING_TO_JSVAL(JS_NewStringCopyZ(cx, asciistring)); break; case UTF8: *vp = STRING_TO_JSVAL(JS_NewStringCopyZ(cx, utfstring)); break; case UCS2: *vp = STRING_TO_JSVAL(JS_NewStringCopyZ(cx, unicodestring)); break; default: JS_ReportWarning(cx, "Unsupported file type %d, proceeding", file->type); } break; case FILE_MODE: SECURITY_CHECK(cx, NULL, "mode", file); JSFILE_CHECK_OPEN("mode"); bytes = JS_malloc(cx, MODE_SIZE); bytes[0] = '\0'; flag = JS_FALSE; if ((file->mode&PR_RDONLY)==PR_RDONLY) { if (flag) strcat(bytes, ","); strcat(bytes, "read"); flag = JS_TRUE; } if ((file->mode&PR_WRONLY)==PR_WRONLY) { if (flag) strcat(bytes, ","); strcat(bytes, "write"); flag = JS_TRUE; } if ((file->mode&PR_RDWR)==PR_RDWR) { if (flag) strcat(bytes, ","); strcat(bytes, "readWrite"); flag = JS_TRUE; } if ((file->mode&PR_APPEND)==PR_APPEND) { if (flag) strcat(bytes, ","); strcat(bytes, "append"); flag = JS_TRUE; } if ((file->mode&PR_CREATE_FILE)==PR_CREATE_FILE) { if (flag) strcat(bytes, ","); strcat(bytes, "create"); flag = JS_TRUE; } if ((file->mode&PR_TRUNCATE)==PR_TRUNCATE) { if (flag) strcat(bytes, ","); strcat(bytes, "replace"); flag = JS_TRUE; } if (file->hasAutoflush) { if (flag) strcat(bytes, ","); strcat(bytes, "hasAutoFlush"); flag = JS_TRUE; } *vp = STRING_TO_JSVAL(JS_NewStringCopyZ(cx, bytes)); JS_free(cx, bytes); break; case FILE_CREATED: SECURITY_CHECK(cx, NULL, "creationTime", file); JSFILE_CHECK_NATIVE("creationTime"); if(((file->isOpen)? PR_GetOpenFileInfo(file->handle, &info): PR_GetFileInfo(file->path, &info))!=PR_SUCCESS){ JS_ReportErrorNumber(cx, JSFile_GetErrorMessage, NULL, JSFILEMSG_CANNOT_ACCESS_FILE_STATUS, file->path); goto out; } PR_ExplodeTime(info.creationTime, PR_LocalTimeParameters,&expandedTime); *vp = OBJECT_TO_JSVAL(js_NewDateObject(cx, expandedTime.tm_year, expandedTime.tm_month, expandedTime.tm_mday, expandedTime.tm_hour, expandedTime.tm_min, expandedTime.tm_sec)); break; case FILE_MODIFIED: SECURITY_CHECK(cx, NULL, "lastModified", file); JSFILE_CHECK_NATIVE("lastModified"); if(((file->isOpen)? PR_GetOpenFileInfo(file->handle, &info): PR_GetFileInfo(file->path, &info))!=PR_SUCCESS){ JS_ReportErrorNumber(cx, JSFile_GetErrorMessage, NULL, JSFILEMSG_CANNOT_ACCESS_FILE_STATUS, file->path); goto out; } PR_ExplodeTime(info.modifyTime, PR_LocalTimeParameters, &expandedTime); *vp = OBJECT_TO_JSVAL(js_NewDateObject(cx, expandedTime.tm_year, expandedTime.tm_month, expandedTime.tm_mday, expandedTime.tm_hour, expandedTime.tm_min, expandedTime.tm_sec)); break; case FILE_SIZE: SECURITY_CHECK(cx, NULL, "size", file); *vp = js_size(cx, file); break; case FILE_LENGTH: SECURITY_CHECK(cx, NULL, "length", file); JSFILE_CHECK_NATIVE("length"); if (js_isDirectory(cx, file)) { /* XXX debug me */ PRDir *dir; PRDirEntry *entry; jsint count = 0; if(!(dir = PR_OpenDir(file->path))){ JS_ReportErrorNumber(cx, JSFile_GetErrorMessage, NULL, JSFILEMSG_CANNOT_OPEN_DIR, file->path); goto out; } while ((entry = PR_ReadDir(dir, PR_SKIP_BOTH))) { count++; } if(!PR_CloseDir(dir)){ JS_ReportErrorNumber(cx, JSFile_GetErrorMessage, NULL, JSFILEMSG_OP_FAILED, "close", file->path); goto out; } *vp = INT_TO_JSVAL(count); break; }else{ /* return file size */ *vp = js_size(cx, file); } break; case FILE_RANDOMACCESS: SECURITY_CHECK(cx, NULL, "hasRandomAccess", file); JSFILE_CHECK_OPEN("hasRandomAccess"); *vp = BOOLEAN_TO_JSVAL(file->hasRandomAccess); break; case FILE_POSITION: SECURITY_CHECK(cx, NULL, "position", file); JSFILE_CHECK_NATIVE("position"); JSFILE_CHECK_OPEN("position"); if(!file->hasRandomAccess){ JS_ReportWarning(cx, "File %s doesn't support random access, can't report the position, proceeding"); *vp = JSVAL_VOID; break; } if (file->isOpen && js_isFile(cx, file)) { int pos = PR_Seek(file->handle, 0, PR_SEEK_CUR); if(pos!=-1){ *vp = INT_TO_JSVAL(pos); }else{ JS_ReportErrorNumber(cx, JSFile_GetErrorMessage, NULL, JSFILEMSG_CANNOT_REPORT_POSITION, file->path); goto out; } }else { JS_ReportWarning(cx, "File %s is closed or not a plain file," " can't report position, proceeding"); goto out; } break; default: SECURITY_CHECK(cx, NULL, "file_access", file); /* this is some other property -- try to use the dir["file"] syntax */ if (js_isDirectory(cx, file)) { PRDir *dir = NULL; PRDirEntry *entry = NULL; char *prop_name; str = JS_ValueToString(cx, id); if (!str) return JS_FALSE; prop_name = JS_GetStringBytes(str); /* no native files past this point */ dir = PR_OpenDir(file->path); if(!dir) { /* This is probably not a directory */ JS_ReportWarning(cx, "Can't open directory %s", file->path); return JS_FALSE; } while ((entry = PR_ReadDir(dir, PR_SKIP_NONE)) != NULL) { if (!strcmp(entry->name, prop_name)){ bytes = js_combinePath(cx, file->path, prop_name); *vp = OBJECT_TO_JSVAL(js_NewFileObject(cx, bytes)); PR_CloseDir(dir); JS_free(cx, bytes); return !JSVAL_IS_NULL(*vp); } } PR_CloseDir(dir); } } return JS_TRUE; out: return JS_FALSE; } static JSBool file_setProperty(JSContext *cx, JSObject *obj, jsval id, jsval *vp) { JSFile *file = JS_GetInstancePrivate(cx, obj, &js_FileClass, NULL); jsint slot; if (JSVAL_IS_STRING(id)){ return JS_TRUE; } slot = JSVAL_TO_INT(id); switch (slot) { /* File.position = 10 */ case FILE_POSITION: SECURITY_CHECK(cx, NULL, "set_position", file); JSFILE_CHECK_NATIVE("set_position"); if(!file->hasRandomAccess){ JS_ReportWarning(cx, "File %s doesn't support random access, can't " "report the position, proceeding"); goto out; } if (file->isOpen && js_isFile(cx, file)) { int32 pos; int32 offset; if (!JS_ValueToInt32(cx, *vp, &offset)){ JS_ReportErrorNumber(cx, JSFile_GetErrorMessage, NULL, JSFILEMSG_FIRST_ARGUMENT_MUST_BE_A_NUMBER, "position", *vp); goto out; } pos = PR_Seek(file->handle, offset, PR_SEEK_SET); if(pos!=-1){ *vp = INT_TO_JSVAL(pos); }else{ JS_ReportErrorNumber(cx, JSFile_GetErrorMessage, NULL, JSFILEMSG_CANNOT_SET_POSITION, file->path); goto out; } } else { JS_ReportWarning(cx, "File %s is closed or not a file, can't set " "position, proceeding", file->path); goto out; } } return JS_TRUE; out: return JS_FALSE; } /* File.currentDir = new File("D:\") or File.currentDir = "D:\" */ static JSBool file_currentDirSetter(JSContext *cx, JSObject *obj, jsval id, jsval *vp) { JSFile *file; file = JS_GetInstancePrivate(cx, obj, &js_FileClass, NULL); /* Look at the rhs and extract a file object from it */ if (JSVAL_IS_OBJECT(*vp)) { if (JS_InstanceOf(cx, obj, &js_FileClass, NULL)) { /* Braindamaged rhs -- just return the old value */ if (file && (!js_exists(cx, file) || !js_isDirectory(cx, file))) { JS_GetProperty(cx, obj, CURRENTDIR_PROPERTY, vp); return JS_FALSE; } else { chdir(file->path); return JS_TRUE; } } else { return JS_FALSE; } } else { JSObject *rhsObject; char *path; path = JS_GetStringBytes(JS_ValueToString(cx, *vp)); rhsObject = js_NewFileObject(cx, path); if (!rhsObject) return JS_FALSE; if (!file || !js_exists(cx, file) || !js_isDirectory(cx, file)){ JS_GetProperty(cx, obj, CURRENTDIR_PROPERTY, vp); } else { *vp = OBJECT_TO_JSVAL(rhsObject); chdir(path); } } return JS_TRUE; } /* Declare class */ JSClass js_FileClass = { "File", JSCLASS_HAS_PRIVATE | JSCLASS_HAS_CACHED_PROTO(JSProto_File), JS_PropertyStub, JS_PropertyStub, file_getProperty, file_setProperty, JS_EnumerateStub, JS_ResolveStub, JS_ConvertStub, file_finalize }; /* -------------------- Functions exposed to the outside -------------------- */ JS_PUBLIC_API(JSObject*) js_InitFileClass(JSContext *cx, JSObject* obj) { JSObject *file, *ctor, *afile; jsval vp; char *currentdir; char separator[2]; file = JS_InitClass(cx, obj, NULL, &js_FileClass, file_constructor, 1, file_props, file_functions, NULL, NULL); if (!file) { JS_ReportErrorNumber(cx, JSFile_GetErrorMessage, NULL, JSFILEMSG_INIT_FAILED); return NULL; } ctor = JS_GetConstructor(cx, file); if (!ctor) return NULL; /* Define CURRENTDIR property. We are doing this to get a slash at the end of the current dir */ afile = js_NewFileObject(cx, CURRENT_DIR); currentdir = JS_malloc(cx, MAX_PATH_LENGTH); currentdir = getcwd(currentdir, MAX_PATH_LENGTH); afile = js_NewFileObject(cx, currentdir); JS_free(cx, currentdir); vp = OBJECT_TO_JSVAL(afile); JS_DefinePropertyWithTinyId(cx, ctor, CURRENTDIR_PROPERTY, 0, vp, JS_PropertyStub, file_currentDirSetter, JSPROP_ENUMERATE | JSPROP_READONLY ); /* Define input */ vp = OBJECT_TO_JSVAL(js_NewFileObjectFromFILE(cx, stdin, STDINPUT_NAME, PR_RDONLY, JS_TRUE, JS_FALSE)); JS_SetProperty(cx, ctor, "input", &vp); /* Define output */ vp = OBJECT_TO_JSVAL(js_NewFileObjectFromFILE(cx, stdout, STDOUTPUT_NAME, PR_WRONLY, JS_TRUE, JS_FALSE)); JS_SetProperty(cx, ctor, "output", &vp); /* Define error */ vp = OBJECT_TO_JSVAL(js_NewFileObjectFromFILE(cx, stderr, STDERROR_NAME, PR_WRONLY, JS_TRUE, JS_FALSE)); JS_SetProperty(cx, ctor, "error", &vp); separator[0] = FILESEPARATOR; separator[1] = '\0'; vp = STRING_TO_JSVAL(JS_NewStringCopyZ(cx, separator)); JS_DefinePropertyWithTinyId(cx, ctor, SEPARATOR_PROPERTY, 0, vp, JS_PropertyStub, JS_PropertyStub, JSPROP_ENUMERATE | JSPROP_READONLY ); return file; } #endif /* JS_HAS_FILE_OBJECT */ pacparser-1.4.5/src/spidermonkey/js/src/jsfile.h000066400000000000000000000041551464010763600216450ustar00rootroot00000000000000/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- * * ***** BEGIN LICENSE BLOCK ***** * Version: MPL 1.1/GPL 2.0/LGPL 2.1 * * The contents of this file are subject to the Mozilla Public License Version * 1.1 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * http://www.mozilla.org/MPL/ * * Software distributed under the License is distributed on an "AS IS" basis, * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License * for the specific language governing rights and limitations under the * License. * * The Original Code is Mozilla Communicator client code, released * March 31, 1998. * * The Initial Developer of the Original Code is * Netscape Communications Corporation. * Portions created by the Initial Developer are Copyright (C) 1998 * the Initial Developer. All Rights Reserved. * * Contributor(s): * * Alternatively, the contents of this file may be used under the terms of * either of the GNU General Public License Version 2 or later (the "GPL"), * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), * in which case the provisions of the GPL or the LGPL are applicable instead * of those above. If you wish to allow use of your version of this file only * under the terms of either the GPL or the LGPL, and not to allow others to * use your version of this file under the terms of the MPL, indicate your * decision by deleting the provisions above and replace them with the notice * and other provisions required by the GPL or the LGPL. If you do not delete * the provisions above, a recipient may use your version of this file under * the terms of any one of the MPL, the GPL or the LGPL. * * ***** END LICENSE BLOCK ***** */ #ifndef _jsfile_h__ #define _jsfile_h__ #if JS_HAS_FILE_OBJECT #include "jsobj.h" extern JS_PUBLIC_API(JSObject*) js_InitFileClass(JSContext *cx, JSObject* obj); extern JS_PUBLIC_API(JSObject*) js_NewFileObject(JSContext *cx, char *bytes); extern JSClass js_FileClass; #endif /* JS_HAS_FILE_OBJECT */ #endif /* _jsfile_h__ */ pacparser-1.4.5/src/spidermonkey/js/src/jsfile.msg000066400000000000000000000162141464010763600222030ustar00rootroot00000000000000/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- * * ***** BEGIN LICENSE BLOCK ***** * Version: MPL 1.1/GPL 2.0/LGPL 2.1 * * The contents of this file are subject to the Mozilla Public License Version * 1.1 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * http://www.mozilla.org/MPL/ * * Software distributed under the License is distributed on an "AS IS" basis, * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License * for the specific language governing rights and limitations under the * License. * * The Original Code is Mozilla Communicator client code, released * March 31, 1998. * * The Initial Developer of the Original Code is * Netscape Communications Corporation. * Portions created by the Initial Developer are Copyright (C) 1998 * the Initial Developer. All Rights Reserved. * * Contributor(s): * * Alternatively, the contents of this file may be used under the terms of * either of the GNU General Public License Version 2 or later (the "GPL"), * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), * in which case the provisions of the GPL or the LGPL are applicable instead * of those above. If you wish to allow use of your version of this file only * under the terms of either the GPL or the LGPL, and not to allow others to * use your version of this file under the terms of the MPL, indicate your * decision by deleting the provisions above and replace them with the notice * and other provisions required by the GPL or the LGPL. If you do not delete * the provisions above, a recipient may use your version of this file under * the terms of any one of the MPL, the GPL or the LGPL. * * ***** END LICENSE BLOCK ***** */ /* Error messages for jsfile.c. See js.msg for format specification. */ MSG_DEF(JSFILEMSG_NOT_AN_ERROR, 0, 0, JSEXN_NONE, "") MSG_DEF(JSFILEMSG_FILE_CONSTRUCTOR_UNDEFINED_ERROR, 1, 0, JSEXN_NONE, "File constructor is undefined") MSG_DEF(JSFILEMSG_FILE_CURRENTDIR_UNDEFINED_ERROR, 2, 0, JSEXN_NONE, "File.currentDir is undefined") MSG_DEF(JSFILEMSG_FIRST_ARGUMENT_OPEN_NOT_STRING_ERROR, 3, 1, JSEXN_NONE, "The first argument {0} to file.open must be a string") MSG_DEF(JSFILEMSG_SECOND_ARGUMENT_OPEN_NOT_STRING_ERROR, 4, 0, JSEXN_NONE, "The second argument to file.open must be a string") MSG_DEF(JSFILEMSG_CANNOT_COPY_FILE_OPEN_FOR_WRITING_ERROR, 5, 1, JSEXN_NONE, "Cannot copy file {0} open for writing") MSG_DEF(JSFILEMSG_CANNOT_ACCESS_FILE_INFO_ERROR, 6, 1, JSEXN_NONE, "Cannot access file information for {0}") MSG_DEF(JSFILEMSG_COPY_READ_ERROR, 7, 1, JSEXN_NONE, "An error occured while attempting to read a file {0} to copy") MSG_DEF(JSFILEMSG_COPY_WRITE_ERROR, 8, 1, JSEXN_NONE, "An error occured while attempting to copy into file {0}") MSG_DEF(JSFILEMSG_EXPECTS_ONE_ARG_ERROR, 9, 0, JSEXN_NONE, "Operation {0} expects one argument, not {1}") MSG_DEF(JSFILEMSG_CANNOT_FLUSH_CLOSE_FILE_ERROR, 10, 1, JSEXN_NONE, "Cannot flush closed file {0}") MSG_DEF(JSFILEMSG_CANNOT_OPEN_WRITING_ERROR, 11, 1, JSEXN_NONE, "Cannot open file {0} for writing") MSG_DEF(JSFILEMSG_WRITEALL_EXPECTS_ONE_ARG_ERROR, 12, 0, JSEXN_NONE, "writeAll expects one argument") MSG_DEF(JSFILEMSG_FIRST_ARGUMENT_WRITEALL_NOT_ARRAY_ERROR, 13, 0, JSEXN_NONE, "writeAll expects an array as an argument") MSG_DEF(JSFILEMSG_UNUSED0, 14, 0, JSEXN_NONE, "Unused error message slot") MSG_DEF(JSFILEMSG_CANNOT_OPEN_FILE_ERROR, 15, 1, JSEXN_NONE, "Cannot open file {0}") MSG_DEF(JSFILEMSG_FIRST_ARGUMENT_CONSTRUCTOR_NOT_STRING_ERROR, 16, 1, JSEXN_NONE, "The argument to the File constructor {0} must be a string") MSG_DEF(JSFILEMSG_BIDIRECTIONAL_PIPE_NOT_SUPPORTED, 17, 0, JSEXN_NONE, "Bidirectional pipes are not supported") MSG_DEF(JSFILEMSG_OPEN_MODE_NOT_SUPPORTED_WITH_PIPES, 18, 2, JSEXN_NONE, "The opening mode you have chosen {0} is not supported by the pipe you are trying to open: {1}") MSG_DEF(JSFILEMSG_OPEN_FAILED, 19, 1, JSEXN_NONE, "open on file {0} failed") MSG_DEF(JSFILEMSG_CLOSE_FAILED, 20, 1, JSEXN_NONE, "close on file {0} failed") MSG_DEF(JSFILEMSG_PCLOSE_FAILED, 21, 1, JSEXN_NONE, "pclose on file {0} failed") MSG_DEF(JSFILEMSG_REMOVE_FAILED, 22, 1, JSEXN_NONE, "remove on file {0} failed") MSG_DEF(JSFILEMSG_CANNOT_ACCESS_FILE_STATUS, 23, 1, JSEXN_NONE, "Cannot access file status for {0}") MSG_DEF(JSFILEMSG_RENAME_FAILED, 24, 2, JSEXN_NONE, "Cannot rename {0} to {1}") MSG_DEF(JSFILEMSG_WRITE_FAILED, 25, 1, JSEXN_NONE, "Write failed on file {0}") MSG_DEF(JSFILEMSG_READ_FAILED, 26, 1, JSEXN_NONE, "Read failed on file {0}") MSG_DEF(JSFILEMSG_SKIP_FAILED, 27, 1, JSEXN_NONE, "Skip failed on file {0}") MSG_DEF(JSFILEMSG_FIRST_ARGUMENT_MUST_BE_A_FUNCTION_OR_REGEX, 28, 1, JSEXN_NONE, "The first argument to file.list must be a function or a regex") MSG_DEF(JSFILEMSG_CANNOT_DO_LIST_ON_A_FILE, 29, 1, JSEXN_NONE, "{0} must be a directory, cannot do list") MSG_DEF(JSFILEMSG_NATIVE_OPERATION_IS_NOT_SUPPORTED, 30, 2, JSEXN_NONE, "Native operation {0} is not supported on {1}") MSG_DEF(JSFILEMSG_CANNOT_SET_PRIVATE_FILE, 31, 1, JSEXN_NONE, "Cannot set private data for file {0}") MSG_DEF(JSFILEMSG_FIRST_ARGUMENT_MUST_BE_A_NUMBER, 32, 2, JSEXN_NONE, "First argument to {0} must be a number, not {1}") MSG_DEF(JSFILEMSG_CANNOT_WRITE, 33, 1, JSEXN_NONE, "Cannot write to {0}, file mode is different") MSG_DEF(JSFILEMSG_CANNOT_READ, 34, 1, JSEXN_NONE, "Cannot read from {0}, file mode is different") MSG_DEF(JSFILEMSG_CANNOT_FLUSH, 35, 1, JSEXN_NONE, "Flush failed on {0}") MSG_DEF(JSFILEMSG_OP_FAILED, 36, 1, JSEXN_NONE, "File operation {0} failed") MSG_DEF(JSFILEMSG_FILE_MUST_BE_OPEN, 37, 1, JSEXN_NONE, "File must be open for {0}") MSG_DEF(JSFILEMSG_FILE_MUST_BE_CLOSED, 38, 1, JSEXN_NONE, "File must be closed for {0}") MSG_DEF(JSFILEMSG_NO_RANDOM_ACCESS, 39, 1, JSEXN_NONE, "File {0} doesn't allow random access") MSG_DEF(JSFILEMSG_OBJECT_CREATION_FAILED, 40, 1, JSEXN_NONE, "Couldn't create {0}") MSG_DEF(JSFILEMSG_CANNOT_OPEN_DIR, 41, 1, JSEXN_NONE, "Couldn't open directory {0}") MSG_DEF(JSFILEMSG_CANNOT_REPORT_POSITION, 42, 1, JSEXN_NONE, "Couldn't report position for {0}") MSG_DEF(JSFILEMSG_CANNOT_SET_POSITION, 43, 1, JSEXN_NONE, "Couldn't set position for {0}") MSG_DEF(JSFILEMSG_INIT_FAILED, 44, 0, JSEXN_NONE, "File class initialization failed") pacparser-1.4.5/src/spidermonkey/js/src/jsfun.c000066400000000000000000002177241464010763600215210ustar00rootroot00000000000000/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- * vim: set ts=8 sw=4 et tw=78: * * ***** BEGIN LICENSE BLOCK ***** * Version: MPL 1.1/GPL 2.0/LGPL 2.1 * * The contents of this file are subject to the Mozilla Public License Version * 1.1 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * http://www.mozilla.org/MPL/ * * Software distributed under the License is distributed on an "AS IS" basis, * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License * for the specific language governing rights and limitations under the * License. * * The Original Code is Mozilla Communicator client code, released * March 31, 1998. * * The Initial Developer of the Original Code is * Netscape Communications Corporation. * Portions created by the Initial Developer are Copyright (C) 1998 * the Initial Developer. All Rights Reserved. * * Contributor(s): * * Alternatively, the contents of this file may be used under the terms of * either of the GNU General Public License Version 2 or later (the "GPL"), * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), * in which case the provisions of the GPL or the LGPL are applicable instead * of those above. If you wish to allow use of your version of this file only * under the terms of either the GPL or the LGPL, and not to allow others to * use your version of this file under the terms of the MPL, indicate your * decision by deleting the provisions above and replace them with the notice * and other provisions required by the GPL or the LGPL. If you do not delete * the provisions above, a recipient may use your version of this file under * the terms of any one of the MPL, the GPL or the LGPL. * * ***** END LICENSE BLOCK ***** */ /* * JS function support. */ #include "jsstddef.h" #include #include "jstypes.h" #include "jsbit.h" #include "jsutil.h" /* Added by JSIFY */ #include "jsapi.h" #include "jsarray.h" #include "jsatom.h" #include "jscntxt.h" #include "jsconfig.h" #include "jsdbgapi.h" #include "jsfun.h" #include "jsgc.h" #include "jsinterp.h" #include "jslock.h" #include "jsnum.h" #include "jsobj.h" #include "jsopcode.h" #include "jsparse.h" #include "jsscan.h" #include "jsscope.h" #include "jsscript.h" #include "jsstr.h" #include "jsexn.h" #if JS_HAS_GENERATORS # include "jsiter.h" #endif /* Generic function/call/arguments tinyids -- also reflected bit numbers. */ enum { CALL_ARGUMENTS = -1, /* predefined arguments local variable */ CALL_CALLEE = -2, /* reference to active function's object */ ARGS_LENGTH = -3, /* number of actual args, arity if inactive */ ARGS_CALLEE = -4, /* reference from arguments to active funobj */ FUN_ARITY = -5, /* number of formal parameters; desired argc */ FUN_NAME = -6, /* function name, "" if anonymous */ FUN_CALLER = -7 /* Function.prototype.caller, backward compat */ }; #if JSFRAME_OVERRIDE_BITS < 8 # error "not enough override bits in JSStackFrame.flags!" #endif #define TEST_OVERRIDE_BIT(fp, tinyid) \ ((fp)->flags & JS_BIT(JSFRAME_OVERRIDE_SHIFT - ((tinyid) + 1))) #define SET_OVERRIDE_BIT(fp, tinyid) \ ((fp)->flags |= JS_BIT(JSFRAME_OVERRIDE_SHIFT - ((tinyid) + 1))) JSBool js_GetArgsValue(JSContext *cx, JSStackFrame *fp, jsval *vp) { JSObject *argsobj; if (TEST_OVERRIDE_BIT(fp, CALL_ARGUMENTS)) { JS_ASSERT(fp->callobj); return OBJ_GET_PROPERTY(cx, fp->callobj, ATOM_TO_JSID(cx->runtime->atomState .argumentsAtom), vp); } argsobj = js_GetArgsObject(cx, fp); if (!argsobj) return JS_FALSE; *vp = OBJECT_TO_JSVAL(argsobj); return JS_TRUE; } static JSBool MarkArgDeleted(JSContext *cx, JSStackFrame *fp, uintN slot) { JSObject *argsobj; jsval bmapval, bmapint; size_t nbits, nbytes; jsbitmap *bitmap; argsobj = fp->argsobj; (void) JS_GetReservedSlot(cx, argsobj, 0, &bmapval); nbits = fp->argc; JS_ASSERT(slot < nbits); if (JSVAL_IS_VOID(bmapval)) { if (nbits <= JSVAL_INT_BITS) { bmapint = 0; bitmap = (jsbitmap *) &bmapint; } else { nbytes = JS_HOWMANY(nbits, JS_BITS_PER_WORD) * sizeof(jsbitmap); bitmap = (jsbitmap *) JS_malloc(cx, nbytes); if (!bitmap) return JS_FALSE; memset(bitmap, 0, nbytes); bmapval = PRIVATE_TO_JSVAL(bitmap); JS_SetReservedSlot(cx, argsobj, 0, bmapval); } } else { if (nbits <= JSVAL_INT_BITS) { bmapint = JSVAL_TO_INT(bmapval); bitmap = (jsbitmap *) &bmapint; } else { bitmap = (jsbitmap *) JSVAL_TO_PRIVATE(bmapval); } } JS_SET_BIT(bitmap, slot); if (bitmap == (jsbitmap *) &bmapint) { bmapval = INT_TO_JSVAL(bmapint); JS_SetReservedSlot(cx, argsobj, 0, bmapval); } return JS_TRUE; } /* NB: Infallible predicate, false does not mean error/exception. */ static JSBool ArgWasDeleted(JSContext *cx, JSStackFrame *fp, uintN slot) { JSObject *argsobj; jsval bmapval, bmapint; jsbitmap *bitmap; argsobj = fp->argsobj; (void) JS_GetReservedSlot(cx, argsobj, 0, &bmapval); if (JSVAL_IS_VOID(bmapval)) return JS_FALSE; if (fp->argc <= JSVAL_INT_BITS) { bmapint = JSVAL_TO_INT(bmapval); bitmap = (jsbitmap *) &bmapint; } else { bitmap = (jsbitmap *) JSVAL_TO_PRIVATE(bmapval); } return JS_TEST_BIT(bitmap, slot) != 0; } JSBool js_GetArgsProperty(JSContext *cx, JSStackFrame *fp, jsid id, JSObject **objp, jsval *vp) { jsval val; JSObject *obj; uintN slot; if (TEST_OVERRIDE_BIT(fp, CALL_ARGUMENTS)) { JS_ASSERT(fp->callobj); if (!OBJ_GET_PROPERTY(cx, fp->callobj, ATOM_TO_JSID(cx->runtime->atomState .argumentsAtom), &val)) { return JS_FALSE; } if (JSVAL_IS_PRIMITIVE(val)) { obj = js_ValueToNonNullObject(cx, val); if (!obj) return JS_FALSE; } else { obj = JSVAL_TO_OBJECT(val); } *objp = obj; return OBJ_GET_PROPERTY(cx, obj, id, vp); } *objp = NULL; *vp = JSVAL_VOID; if (JSID_IS_INT(id)) { slot = (uintN) JSID_TO_INT(id); if (slot < fp->argc) { if (fp->argsobj && ArgWasDeleted(cx, fp, slot)) return OBJ_GET_PROPERTY(cx, fp->argsobj, id, vp); *vp = fp->argv[slot]; } else { /* * Per ECMA-262 Ed. 3, 10.1.8, last bulleted item, do not share * storage between the formal parameter and arguments[k] for all * k >= fp->argc && k < fp->fun->nargs. For example, in * * function f(x) { x = 42; return arguments[0]; } * f(); * * the call to f should return undefined, not 42. If fp->argsobj * is null at this point, as it would be in the example, return * undefined in *vp. */ if (fp->argsobj) return OBJ_GET_PROPERTY(cx, fp->argsobj, id, vp); } } else { if (id == ATOM_TO_JSID(cx->runtime->atomState.lengthAtom)) { if (fp->argsobj && TEST_OVERRIDE_BIT(fp, ARGS_LENGTH)) return OBJ_GET_PROPERTY(cx, fp->argsobj, id, vp); *vp = INT_TO_JSVAL((jsint) fp->argc); } } return JS_TRUE; } JSObject * js_GetArgsObject(JSContext *cx, JSStackFrame *fp) { JSObject *argsobj, *global, *parent; /* * We must be in a function activation; the function must be lightweight * or else fp must have a variable object. */ JS_ASSERT(fp->fun && (!(fp->fun->flags & JSFUN_HEAVYWEIGHT) || fp->varobj)); /* Skip eval and debugger frames. */ while (fp->flags & JSFRAME_SPECIAL) fp = fp->down; /* Create an arguments object for fp only if it lacks one. */ argsobj = fp->argsobj; if (argsobj) return argsobj; /* Link the new object to fp so it can get actual argument values. */ argsobj = js_NewObject(cx, &js_ArgumentsClass, NULL, NULL); if (!argsobj || !JS_SetPrivate(cx, argsobj, fp)) { cx->weakRoots.newborn[GCX_OBJECT] = NULL; return NULL; } /* * Give arguments an intrinsic scope chain link to fp's global object. * Since the arguments object lacks a prototype because js_ArgumentsClass * is not initialized, js_NewObject won't assign a default parent to it. * * Therefore if arguments is used as the head of an eval scope chain (via * a direct or indirect call to eval(program, arguments)), any reference * to a standard class object in the program will fail to resolve due to * js_GetClassPrototype not being able to find a global object containing * the standard prototype by starting from arguments and following parent. */ global = fp->scopeChain; while ((parent = OBJ_GET_PARENT(cx, global)) != NULL) global = parent; argsobj->slots[JSSLOT_PARENT] = OBJECT_TO_JSVAL(global); fp->argsobj = argsobj; return argsobj; } static JSBool args_enumerate(JSContext *cx, JSObject *obj); JSBool js_PutArgsObject(JSContext *cx, JSStackFrame *fp) { JSObject *argsobj; jsval bmapval, rval; JSBool ok; JSRuntime *rt; /* * Reuse args_enumerate here to reflect fp's actual arguments as indexed * elements of argsobj. Do this first, before clearing and freeing the * deleted argument slot bitmap, because args_enumerate depends on that. */ argsobj = fp->argsobj; ok = args_enumerate(cx, argsobj); /* * Now clear the deleted argument number bitmap slot and free the bitmap, * if one was actually created due to 'delete arguments[0]' or similar. */ (void) JS_GetReservedSlot(cx, argsobj, 0, &bmapval); if (!JSVAL_IS_VOID(bmapval)) { JS_SetReservedSlot(cx, argsobj, 0, JSVAL_VOID); if (fp->argc > JSVAL_INT_BITS) JS_free(cx, JSVAL_TO_PRIVATE(bmapval)); } /* * Now get the prototype properties so we snapshot fp->fun and fp->argc * before fp goes away. */ rt = cx->runtime; ok &= js_GetProperty(cx, argsobj, ATOM_TO_JSID(rt->atomState.calleeAtom), &rval); ok &= js_SetProperty(cx, argsobj, ATOM_TO_JSID(rt->atomState.calleeAtom), &rval); ok &= js_GetProperty(cx, argsobj, ATOM_TO_JSID(rt->atomState.lengthAtom), &rval); ok &= js_SetProperty(cx, argsobj, ATOM_TO_JSID(rt->atomState.lengthAtom), &rval); /* * Clear the private pointer to fp, which is about to go away (js_Invoke). * Do this last because the args_enumerate and js_GetProperty calls above * need to follow the private slot to find fp. */ ok &= JS_SetPrivate(cx, argsobj, NULL); fp->argsobj = NULL; return ok; } static JSBool args_delProperty(JSContext *cx, JSObject *obj, jsval id, jsval *vp) { jsint slot; JSStackFrame *fp; if (!JSVAL_IS_INT(id)) return JS_TRUE; fp = (JSStackFrame *) JS_GetInstancePrivate(cx, obj, &js_ArgumentsClass, NULL); if (!fp) return JS_TRUE; JS_ASSERT(fp->argsobj); slot = JSVAL_TO_INT(id); switch (slot) { case ARGS_CALLEE: case ARGS_LENGTH: SET_OVERRIDE_BIT(fp, slot); break; default: if ((uintN)slot < fp->argc && !MarkArgDeleted(cx, fp, slot)) return JS_FALSE; break; } return JS_TRUE; } static JSBool args_getProperty(JSContext *cx, JSObject *obj, jsval id, jsval *vp) { jsint slot; JSStackFrame *fp; if (!JSVAL_IS_INT(id)) return JS_TRUE; fp = (JSStackFrame *) JS_GetInstancePrivate(cx, obj, &js_ArgumentsClass, NULL); if (!fp) return JS_TRUE; JS_ASSERT(fp->argsobj); slot = JSVAL_TO_INT(id); switch (slot) { case ARGS_CALLEE: if (!TEST_OVERRIDE_BIT(fp, slot)) *vp = fp->argv ? fp->argv[-2] : OBJECT_TO_JSVAL(fp->fun->object); break; case ARGS_LENGTH: if (!TEST_OVERRIDE_BIT(fp, slot)) *vp = INT_TO_JSVAL((jsint)fp->argc); break; default: if ((uintN)slot < fp->argc && !ArgWasDeleted(cx, fp, slot)) *vp = fp->argv[slot]; break; } return JS_TRUE; } static JSBool args_setProperty(JSContext *cx, JSObject *obj, jsval id, jsval *vp) { JSStackFrame *fp; jsint slot; if (!JSVAL_IS_INT(id)) return JS_TRUE; fp = (JSStackFrame *) JS_GetInstancePrivate(cx, obj, &js_ArgumentsClass, NULL); if (!fp) return JS_TRUE; JS_ASSERT(fp->argsobj); slot = JSVAL_TO_INT(id); switch (slot) { case ARGS_CALLEE: case ARGS_LENGTH: SET_OVERRIDE_BIT(fp, slot); break; default: if (FUN_INTERPRETED(fp->fun) && (uintN)slot < fp->argc && !ArgWasDeleted(cx, fp, slot)) { fp->argv[slot] = *vp; } break; } return JS_TRUE; } static JSBool args_resolve(JSContext *cx, JSObject *obj, jsval id, uintN flags, JSObject **objp) { JSStackFrame *fp; uintN slot; JSString *str; JSAtom *atom; intN tinyid; jsval value; *objp = NULL; fp = (JSStackFrame *) JS_GetInstancePrivate(cx, obj, &js_ArgumentsClass, NULL); if (!fp) return JS_TRUE; JS_ASSERT(fp->argsobj); if (JSVAL_IS_INT(id)) { slot = JSVAL_TO_INT(id); if (slot < fp->argc && !ArgWasDeleted(cx, fp, slot)) { /* XXX ECMA specs DontEnum, contrary to other array-like objects */ if (!js_DefineProperty(cx, obj, INT_JSVAL_TO_JSID(id), fp->argv[slot], args_getProperty, args_setProperty, JS_VERSION_IS_ECMA(cx) ? 0 : JSPROP_ENUMERATE, NULL)) { return JS_FALSE; } *objp = obj; } } else { str = JSVAL_TO_STRING(id); atom = cx->runtime->atomState.lengthAtom; if (str == ATOM_TO_STRING(atom)) { tinyid = ARGS_LENGTH; value = INT_TO_JSVAL(fp->argc); } else { atom = cx->runtime->atomState.calleeAtom; if (str == ATOM_TO_STRING(atom)) { tinyid = ARGS_CALLEE; value = fp->argv ? fp->argv[-2] : OBJECT_TO_JSVAL(fp->fun->object); } else { atom = NULL; /* Quell GCC overwarnings. */ tinyid = 0; value = JSVAL_NULL; } } if (atom && !TEST_OVERRIDE_BIT(fp, tinyid)) { if (!js_DefineNativeProperty(cx, obj, ATOM_TO_JSID(atom), value, args_getProperty, args_setProperty, 0, SPROP_HAS_SHORTID, tinyid, NULL)) { return JS_FALSE; } *objp = obj; } } return JS_TRUE; } static JSBool args_enumerate(JSContext *cx, JSObject *obj) { JSStackFrame *fp; JSObject *pobj; JSProperty *prop; uintN slot, argc; fp = (JSStackFrame *) JS_GetInstancePrivate(cx, obj, &js_ArgumentsClass, NULL); if (!fp) return JS_TRUE; JS_ASSERT(fp->argsobj); /* * Trigger reflection with value snapshot in args_resolve using a series * of js_LookupProperty calls. We handle length, callee, and the indexed * argument properties. We know that args_resolve covers all these cases * and creates direct properties of obj, but that it may fail to resolve * length or callee if overridden. */ if (!js_LookupProperty(cx, obj, ATOM_TO_JSID(cx->runtime->atomState.lengthAtom), &pobj, &prop)) { return JS_FALSE; } if (prop) OBJ_DROP_PROPERTY(cx, pobj, prop); if (!js_LookupProperty(cx, obj, ATOM_TO_JSID(cx->runtime->atomState.calleeAtom), &pobj, &prop)) { return JS_FALSE; } if (prop) OBJ_DROP_PROPERTY(cx, pobj, prop); argc = fp->argc; for (slot = 0; slot < argc; slot++) { if (!js_LookupProperty(cx, obj, INT_TO_JSID((jsint)slot), &pobj, &prop)) return JS_FALSE; if (prop) OBJ_DROP_PROPERTY(cx, pobj, prop); } return JS_TRUE; } #if JS_HAS_GENERATORS /* * If a generator-iterator's arguments or call object escapes, it needs to * mark its generator object. */ static uint32 args_or_call_mark(JSContext *cx, JSObject *obj, void *arg) { JSStackFrame *fp; fp = JS_GetPrivate(cx, obj); if (fp && (fp->flags & JSFRAME_GENERATOR)) GC_MARK(cx, FRAME_TO_GENERATOR(fp)->obj, "FRAME_TO_GENERATOR(fp)->obj"); return 0; } #else # define args_or_call_mark NULL #endif /* * The Arguments class is not initialized via JS_InitClass, and must not be, * because its name is "Object". Per ECMA, that causes instances of it to * delegate to the object named by Object.prototype. It also ensures that * arguments.toString() returns "[object Object]". * * The JSClass functions below collaborate to lazily reflect and synchronize * actual argument values, argument count, and callee function object stored * in a JSStackFrame with their corresponding property values in the frame's * arguments object. */ JSClass js_ArgumentsClass = { js_Object_str, JSCLASS_HAS_PRIVATE | JSCLASS_NEW_RESOLVE | JSCLASS_HAS_RESERVED_SLOTS(1) | JSCLASS_HAS_CACHED_PROTO(JSProto_Object), JS_PropertyStub, args_delProperty, args_getProperty, args_setProperty, args_enumerate, (JSResolveOp) args_resolve, JS_ConvertStub, JS_FinalizeStub, NULL, NULL, NULL, NULL, NULL, NULL, args_or_call_mark, NULL }; JSObject * js_GetCallObject(JSContext *cx, JSStackFrame *fp, JSObject *parent) { JSObject *callobj, *funobj; /* Create a call object for fp only if it lacks one. */ JS_ASSERT(fp->fun); callobj = fp->callobj; if (callobj) return callobj; JS_ASSERT(fp->fun); /* The default call parent is its function's parent (static link). */ if (!parent) { funobj = fp->argv ? JSVAL_TO_OBJECT(fp->argv[-2]) : fp->fun->object; if (funobj) parent = OBJ_GET_PARENT(cx, funobj); } /* Create the call object and link it to its stack frame. */ callobj = js_NewObject(cx, &js_CallClass, NULL, parent); if (!callobj || !JS_SetPrivate(cx, callobj, fp)) { cx->weakRoots.newborn[GCX_OBJECT] = NULL; return NULL; } fp->callobj = callobj; /* Make callobj be the scope chain and the variables object. */ JS_ASSERT(fp->scopeChain == parent); fp->scopeChain = callobj; fp->varobj = callobj; return callobj; } static JSBool call_enumerate(JSContext *cx, JSObject *obj); JSBool js_PutCallObject(JSContext *cx, JSStackFrame *fp) { JSObject *callobj; JSBool ok; jsid argsid; jsval aval; /* * Reuse call_enumerate here to reflect all actual args and vars into the * call object from fp. */ callobj = fp->callobj; if (!callobj) return JS_TRUE; ok = call_enumerate(cx, callobj); /* * Get the arguments object to snapshot fp's actual argument values. */ if (fp->argsobj) { argsid = ATOM_TO_JSID(cx->runtime->atomState.argumentsAtom); ok &= js_GetProperty(cx, callobj, argsid, &aval); ok &= js_SetProperty(cx, callobj, argsid, &aval); ok &= js_PutArgsObject(cx, fp); } /* * Clear the private pointer to fp, which is about to go away (js_Invoke). * Do this last because the call_enumerate and js_GetProperty calls above * need to follow the private slot to find fp. */ ok &= JS_SetPrivate(cx, callobj, NULL); fp->callobj = NULL; return ok; } static JSPropertySpec call_props[] = { {js_arguments_str, CALL_ARGUMENTS, JSPROP_PERMANENT,0,0}, {"__callee__", CALL_CALLEE, 0,0,0}, {0,0,0,0,0} }; static JSBool call_getProperty(JSContext *cx, JSObject *obj, jsval id, jsval *vp) { JSStackFrame *fp; jsint slot; if (!JSVAL_IS_INT(id)) return JS_TRUE; fp = (JSStackFrame *) JS_GetPrivate(cx, obj); if (!fp) return JS_TRUE; JS_ASSERT(fp->fun); slot = JSVAL_TO_INT(id); switch (slot) { case CALL_ARGUMENTS: if (!TEST_OVERRIDE_BIT(fp, slot)) { JSObject *argsobj = js_GetArgsObject(cx, fp); if (!argsobj) return JS_FALSE; *vp = OBJECT_TO_JSVAL(argsobj); } break; case CALL_CALLEE: if (!TEST_OVERRIDE_BIT(fp, slot)) *vp = fp->argv ? fp->argv[-2] : OBJECT_TO_JSVAL(fp->fun->object); break; default: if ((uintN)slot < JS_MAX(fp->argc, fp->fun->nargs)) *vp = fp->argv[slot]; break; } return JS_TRUE; } static JSBool call_setProperty(JSContext *cx, JSObject *obj, jsval id, jsval *vp) { JSStackFrame *fp; jsint slot; if (!JSVAL_IS_INT(id)) return JS_TRUE; fp = (JSStackFrame *) JS_GetPrivate(cx, obj); if (!fp) return JS_TRUE; JS_ASSERT(fp->fun); slot = JSVAL_TO_INT(id); switch (slot) { case CALL_ARGUMENTS: case CALL_CALLEE: SET_OVERRIDE_BIT(fp, slot); break; default: if ((uintN)slot < JS_MAX(fp->argc, fp->fun->nargs)) fp->argv[slot] = *vp; break; } return JS_TRUE; } JSBool js_GetCallVariable(JSContext *cx, JSObject *obj, jsval id, jsval *vp) { JSStackFrame *fp; JS_ASSERT(JSVAL_IS_INT(id)); fp = (JSStackFrame *) JS_GetPrivate(cx, obj); if (fp) { /* XXX no jsint slot commoning here to avoid MSVC1.52 crashes */ if ((uintN)JSVAL_TO_INT(id) < fp->nvars) *vp = fp->vars[JSVAL_TO_INT(id)]; } return JS_TRUE; } JSBool js_SetCallVariable(JSContext *cx, JSObject *obj, jsval id, jsval *vp) { JSStackFrame *fp; JS_ASSERT(JSVAL_IS_INT(id)); fp = (JSStackFrame *) JS_GetPrivate(cx, obj); if (fp) { /* XXX jsint slot is block-local here to avoid MSVC1.52 crashes */ jsint slot = JSVAL_TO_INT(id); if ((uintN)slot < fp->nvars) fp->vars[slot] = *vp; } return JS_TRUE; } static JSBool call_enumerate(JSContext *cx, JSObject *obj) { JSStackFrame *fp; JSObject *funobj, *pobj; JSScope *scope; JSScopeProperty *sprop, *cprop; JSPropertyOp getter; jsval *vec; JSAtom *atom; JSProperty *prop; fp = (JSStackFrame *) JS_GetPrivate(cx, obj); if (!fp) return JS_TRUE; /* * Do not enumerate a cloned function object at fp->argv[-2], it may have * gained its own (mutable) scope (e.g., a brutally-shared XUL script sets * the clone's prototype property). We must enumerate the function object * that was decorated with parameter and local variable properties by the * compiler when the compiler created fp->fun, namely fp->fun->object. * * Contrast with call_resolve, where we prefer fp->argv[-2], because we'll * use js_LookupProperty to find any overridden properties in that object, * if it was a mutated clone; and if not, we will search its prototype, * fp->fun->object, to find compiler-created params and locals. */ funobj = fp->fun->object; if (!funobj) return JS_TRUE; /* * Reflect actual args from fp->argv for formal parameters, and local vars * and functions in fp->vars for declared variables and nested-at-top-level * local functions. */ scope = OBJ_SCOPE(funobj); for (sprop = SCOPE_LAST_PROP(scope); sprop; sprop = sprop->parent) { getter = sprop->getter; if (getter == js_GetArgument) vec = fp->argv; else if (getter == js_GetLocalVariable) vec = fp->vars; else continue; /* Trigger reflection by looking up the unhidden atom for sprop->id. */ JS_ASSERT(JSID_IS_ATOM(sprop->id)); atom = JSID_TO_ATOM(sprop->id); JS_ASSERT(atom->flags & ATOM_HIDDEN); atom = atom->entry.value; if (!js_LookupProperty(cx, obj, ATOM_TO_JSID(atom), &pobj, &prop)) return JS_FALSE; /* * If we found the property in a different object, don't try sticking * it into wrong slots vector. This can occur because we have a mutable * __proto__ slot, and cloned function objects rely on their __proto__ * to delegate to the object that contains the var and arg properties. */ if (!prop || pobj != obj) { if (prop) OBJ_DROP_PROPERTY(cx, pobj, prop); continue; } cprop = (JSScopeProperty *)prop; LOCKED_OBJ_SET_SLOT(obj, cprop->slot, vec[(uint16) sprop->shortid]); OBJ_DROP_PROPERTY(cx, obj, prop); } return JS_TRUE; } static JSBool call_resolve(JSContext *cx, JSObject *obj, jsval id, uintN flags, JSObject **objp) { JSStackFrame *fp; JSObject *funobj; JSString *str; JSAtom *atom; JSObject *obj2; JSProperty *prop; JSScopeProperty *sprop; JSPropertyOp getter, setter; uintN attrs, slot, nslots, spflags; jsval *vp, value; intN shortid; fp = (JSStackFrame *) JS_GetPrivate(cx, obj); if (!fp) return JS_TRUE; JS_ASSERT(fp->fun); if (!JSVAL_IS_STRING(id)) return JS_TRUE; funobj = fp->argv ? JSVAL_TO_OBJECT(fp->argv[-2]) : fp->fun->object; if (!funobj) return JS_TRUE; JS_ASSERT((JSFunction *) JS_GetPrivate(cx, funobj) == fp->fun); str = JSVAL_TO_STRING(id); atom = js_AtomizeString(cx, str, 0); if (!atom) return JS_FALSE; if (!js_LookupHiddenProperty(cx, funobj, ATOM_TO_JSID(atom), &obj2, &prop)) return JS_FALSE; if (prop) { if (!OBJ_IS_NATIVE(obj2)) { OBJ_DROP_PROPERTY(cx, obj2, prop); return JS_TRUE; } sprop = (JSScopeProperty *) prop; getter = sprop->getter; attrs = sprop->attrs & ~JSPROP_SHARED; slot = (uintN) sprop->shortid; OBJ_DROP_PROPERTY(cx, obj2, prop); /* Ensure we found an arg or var property for the same function. */ if ((sprop->flags & SPROP_IS_HIDDEN) && (obj2 == funobj || (JSFunction *) JS_GetPrivate(cx, obj2) == fp->fun)) { if (getter == js_GetArgument) { vp = fp->argv; nslots = JS_MAX(fp->argc, fp->fun->nargs); getter = setter = NULL; } else { JS_ASSERT(getter == js_GetLocalVariable); vp = fp->vars; nslots = fp->nvars; getter = js_GetCallVariable; setter = js_SetCallVariable; } if (slot < nslots) { value = vp[slot]; spflags = SPROP_HAS_SHORTID; shortid = (intN) slot; } else { value = JSVAL_VOID; spflags = 0; shortid = 0; } if (!js_DefineNativeProperty(cx, obj, ATOM_TO_JSID(atom), value, getter, setter, attrs, spflags, shortid, NULL)) { return JS_FALSE; } *objp = obj; } } return JS_TRUE; } static JSBool call_convert(JSContext *cx, JSObject *obj, JSType type, jsval *vp) { JSStackFrame *fp; if (type == JSTYPE_FUNCTION) { fp = (JSStackFrame *) JS_GetPrivate(cx, obj); if (fp) { JS_ASSERT(fp->fun); *vp = fp->argv ? fp->argv[-2] : OBJECT_TO_JSVAL(fp->fun->object); } } return JS_TRUE; } JSClass js_CallClass = { js_Call_str, JSCLASS_HAS_PRIVATE | JSCLASS_NEW_RESOLVE | JSCLASS_IS_ANONYMOUS | JSCLASS_HAS_CACHED_PROTO(JSProto_Call), JS_PropertyStub, JS_PropertyStub, call_getProperty, call_setProperty, call_enumerate, (JSResolveOp)call_resolve, call_convert, JS_FinalizeStub, NULL, NULL, NULL, NULL, NULL, NULL, args_or_call_mark, NULL, }; /* * ECMA-262 specifies that length is a property of function object instances, * but we can avoid that space cost by delegating to a prototype property that * is JSPROP_PERMANENT and JSPROP_SHARED. Each fun_getProperty call computes * a fresh length value based on the arity of the individual function object's * private data. * * The extensions below other than length, i.e., the ones not in ECMA-262, * are neither JSPROP_READONLY nor JSPROP_SHARED, because for compatibility * with ECMA we must allow a delegating object to override them. */ #define LENGTH_PROP_ATTRS (JSPROP_READONLY|JSPROP_PERMANENT|JSPROP_SHARED) static JSPropertySpec function_props[] = { {js_arguments_str, CALL_ARGUMENTS, JSPROP_PERMANENT, 0,0}, {js_arity_str, FUN_ARITY, JSPROP_PERMANENT, 0,0}, {js_caller_str, FUN_CALLER, JSPROP_PERMANENT, 0,0}, {js_length_str, ARGS_LENGTH, LENGTH_PROP_ATTRS, 0,0}, {js_name_str, FUN_NAME, JSPROP_PERMANENT, 0,0}, {0,0,0,0,0} }; static JSBool fun_getProperty(JSContext *cx, JSObject *obj, jsval id, jsval *vp) { jsint slot; JSFunction *fun; JSStackFrame *fp; if (!JSVAL_IS_INT(id)) return JS_TRUE; slot = JSVAL_TO_INT(id); /* * Loop because getter and setter can be delegated from another class, * but loop only for ARGS_LENGTH because we must pretend that f.length * is in each function instance f, per ECMA-262, instead of only in the * Function.prototype object (we use JSPROP_PERMANENT with JSPROP_SHARED * to make it appear so). * * This code couples tightly to the attributes for the function_props[] * initializers above, and to js_SetProperty and js_HasOwnPropertyHelper. * * It's important to allow delegating objects, even though they inherit * this getter (fun_getProperty), to override arguments, arity, caller, * and name. If we didn't return early for slot != ARGS_LENGTH, we would * clobber *vp with the native property value, instead of letting script * override that value in delegating objects. * * Note how that clobbering is what simulates JSPROP_READONLY for all of * the non-standard properties when the directly addressed object (obj) * is a function object (i.e., when this loop does not iterate). */ while (!(fun = (JSFunction *) JS_GetInstancePrivate(cx, obj, &js_FunctionClass, NULL))) { if (slot != ARGS_LENGTH) return JS_TRUE; obj = OBJ_GET_PROTO(cx, obj); if (!obj) return JS_TRUE; } /* Find fun's top-most activation record. */ for (fp = cx->fp; fp && (fp->fun != fun || (fp->flags & JSFRAME_SPECIAL)); fp = fp->down) { continue; } switch (slot) { case CALL_ARGUMENTS: /* Warn if strict about f.arguments or equivalent unqualified uses. */ if (!JS_ReportErrorFlagsAndNumber(cx, JSREPORT_WARNING | JSREPORT_STRICT, js_GetErrorMessage, NULL, JSMSG_DEPRECATED_USAGE, js_arguments_str)) { return JS_FALSE; } if (fp) { if (!js_GetArgsValue(cx, fp, vp)) return JS_FALSE; } else { *vp = JSVAL_NULL; } break; case ARGS_LENGTH: case FUN_ARITY: *vp = INT_TO_JSVAL((jsint)fun->nargs); break; case FUN_NAME: *vp = fun->atom ? ATOM_KEY(fun->atom) : STRING_TO_JSVAL(cx->runtime->emptyString); break; case FUN_CALLER: while (fp && (fp->flags & JSFRAME_SKIP_CALLER) && fp->down) fp = fp->down; if (fp && fp->down && fp->down->fun && fp->down->argv) *vp = fp->down->argv[-2]; else *vp = JSVAL_NULL; if (!JSVAL_IS_PRIMITIVE(*vp) && cx->runtime->checkObjectAccess) { id = ATOM_KEY(cx->runtime->atomState.callerAtom); if (!cx->runtime->checkObjectAccess(cx, obj, id, JSACC_READ, vp)) return JS_FALSE; } break; default: /* XXX fun[0] and fun.arguments[0] are equivalent. */ if (fp && fp->fun && (uintN)slot < fp->fun->nargs) *vp = fp->argv[slot]; break; } return JS_TRUE; } static JSBool fun_enumerate(JSContext *cx, JSObject *obj) { jsid prototypeId; JSObject *pobj; JSProperty *prop; prototypeId = ATOM_TO_JSID(cx->runtime->atomState.classPrototypeAtom); if (!OBJ_LOOKUP_PROPERTY(cx, obj, prototypeId, &pobj, &prop)) return JS_FALSE; if (prop) OBJ_DROP_PROPERTY(cx, pobj, prop); return JS_TRUE; } static JSBool fun_resolve(JSContext *cx, JSObject *obj, jsval id, uintN flags, JSObject **objp) { JSFunction *fun; JSString *str; JSAtom *prototypeAtom; /* * No need to reflect fun.prototype in 'fun.prototype = ...' or in an * unqualified reference to prototype, which the emitter looks up as a * hidden atom when attempting to bind to a formal parameter or local * variable slot. */ if (flags & (JSRESOLVE_ASSIGNING | JSRESOLVE_HIDDEN)) return JS_TRUE; if (!JSVAL_IS_STRING(id)) return JS_TRUE; /* No valid function object should lack private data, but check anyway. */ fun = (JSFunction *)JS_GetInstancePrivate(cx, obj, &js_FunctionClass, NULL); if (!fun || !fun->object) return JS_TRUE; /* * Ok, check whether id is 'prototype' and bootstrap the function object's * prototype property. */ str = JSVAL_TO_STRING(id); prototypeAtom = cx->runtime->atomState.classPrototypeAtom; if (str == ATOM_TO_STRING(prototypeAtom)) { JSObject *proto, *parentProto; jsval pval; proto = parentProto = NULL; if (fun->object != obj && fun->object) { /* * Clone of a function: make its prototype property value have the * same class as the clone-parent's prototype. */ if (!OBJ_GET_PROPERTY(cx, fun->object, ATOM_TO_JSID(prototypeAtom), &pval)) { return JS_FALSE; } if (!JSVAL_IS_PRIMITIVE(pval)) { /* * We are about to allocate a new object, so hack the newborn * root until then to protect pval in case it is figuratively * up in the air, with no strong refs protecting it. */ cx->weakRoots.newborn[GCX_OBJECT] = JSVAL_TO_GCTHING(pval); parentProto = JSVAL_TO_OBJECT(pval); } } /* * Beware of the wacky case of a user function named Object -- trying * to find a prototype for that will recur back here _ad perniciem_. */ if (!parentProto && fun->atom == CLASS_ATOM(cx, Object)) return JS_TRUE; /* * If resolving "prototype" in a clone, clone the parent's prototype. * Pass the constructor's (obj's) parent as the prototype parent, to * avoid defaulting to parentProto.constructor.__parent__. */ proto = js_NewObject(cx, &js_ObjectClass, parentProto, OBJ_GET_PARENT(cx, obj)); if (!proto) return JS_FALSE; /* * ECMA (15.3.5.2) says that constructor.prototype is DontDelete for * user-defined functions, but DontEnum | ReadOnly | DontDelete for * native "system" constructors such as Object or Function. So lazily * set the former here in fun_resolve, but eagerly define the latter * in JS_InitClass, with the right attributes. */ if (!js_SetClassPrototype(cx, obj, proto, JSPROP_ENUMERATE | JSPROP_PERMANENT)) { cx->weakRoots.newborn[GCX_OBJECT] = NULL; return JS_FALSE; } *objp = obj; } return JS_TRUE; } static JSBool fun_convert(JSContext *cx, JSObject *obj, JSType type, jsval *vp) { switch (type) { case JSTYPE_FUNCTION: *vp = OBJECT_TO_JSVAL(obj); return JS_TRUE; default: return js_TryValueOf(cx, obj, type, vp); } } static void fun_finalize(JSContext *cx, JSObject *obj) { JSFunction *fun; JSScript *script; /* No valid function object should lack private data, but check anyway. */ fun = (JSFunction *) JS_GetPrivate(cx, obj); if (!fun) return; if (fun->object == obj) fun->object = NULL; /* Null-check required since the parser sets interpreted very early. */ if (FUN_INTERPRETED(fun) && fun->u.i.script && js_IsAboutToBeFinalized(cx, fun)) { script = fun->u.i.script; fun->u.i.script = NULL; js_DestroyScript(cx, script); } } #if JS_HAS_XDR #include "jsxdrapi.h" enum { JSXDR_FUNARG = 1, JSXDR_FUNVAR = 2, JSXDR_FUNCONST = 3 }; /* XXX store parent and proto, if defined */ static JSBool fun_xdrObject(JSXDRState *xdr, JSObject **objp) { JSContext *cx; JSFunction *fun; uint32 nullAtom; /* flag to indicate if fun->atom is NULL */ JSTempValueRooter tvr; uint32 flagsword; /* originally only flags was JS_XDRUint8'd */ uint16 extraUnused; /* variable for no longer used field */ JSAtom *propAtom; JSScopeProperty *sprop; uint32 userid; /* NB: holds a signed int-tagged jsval */ uintN i, n, dupflag; uint32 type; JSBool ok; #ifdef DEBUG uintN nvars = 0, nargs = 0; #endif cx = xdr->cx; if (xdr->mode == JSXDR_ENCODE) { /* * No valid function object should lack private data, but fail soft * (return true, no error report) in case one does due to API pilot * or internal error. */ fun = (JSFunction *) JS_GetPrivate(cx, *objp); if (!fun) return JS_TRUE; if (!FUN_INTERPRETED(fun)) { JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_NOT_SCRIPTED_FUNCTION, JS_GetFunctionName(fun)); return JS_FALSE; } nullAtom = !fun->atom; flagsword = ((uint32)fun->u.i.nregexps << 16) | fun->flags; extraUnused = 0; } else { fun = js_NewFunction(cx, NULL, NULL, 0, 0, NULL, NULL); if (!fun) return JS_FALSE; } /* From here on, control flow must flow through label out. */ JS_PUSH_TEMP_ROOT_OBJECT(cx, fun->object, &tvr); ok = JS_TRUE; if (!JS_XDRUint32(xdr, &nullAtom)) goto bad; if (!nullAtom && !js_XDRStringAtom(xdr, &fun->atom)) goto bad; if (!JS_XDRUint16(xdr, &fun->nargs) || !JS_XDRUint16(xdr, &extraUnused) || !JS_XDRUint16(xdr, &fun->u.i.nvars) || !JS_XDRUint32(xdr, &flagsword)) { goto bad; } /* Assert that all previous writes of extraUnused were writes of 0. */ JS_ASSERT(extraUnused == 0); /* do arguments and local vars */ if (fun->object) { n = fun->nargs + fun->u.i.nvars; if (xdr->mode == JSXDR_ENCODE) { JSScope *scope; JSScopeProperty **spvec, *auto_spvec[8]; void *mark; if (n <= sizeof auto_spvec / sizeof auto_spvec[0]) { spvec = auto_spvec; mark = NULL; } else { mark = JS_ARENA_MARK(&cx->tempPool); JS_ARENA_ALLOCATE_CAST(spvec, JSScopeProperty **, &cx->tempPool, n * sizeof(JSScopeProperty *)); if (!spvec) { JS_ReportOutOfMemory(cx); goto bad; } } scope = OBJ_SCOPE(fun->object); for (sprop = SCOPE_LAST_PROP(scope); sprop; sprop = sprop->parent) { if (sprop->getter == js_GetArgument) { JS_ASSERT(nargs++ <= fun->nargs); spvec[sprop->shortid] = sprop; } else if (sprop->getter == js_GetLocalVariable) { JS_ASSERT(nvars++ <= fun->u.i.nvars); spvec[fun->nargs + sprop->shortid] = sprop; } } for (i = 0; i < n; i++) { sprop = spvec[i]; JS_ASSERT(sprop->flags & SPROP_HAS_SHORTID); type = (i < fun->nargs) ? JSXDR_FUNARG : (sprop->attrs & JSPROP_READONLY) ? JSXDR_FUNCONST : JSXDR_FUNVAR; userid = INT_TO_JSVAL(sprop->shortid); propAtom = JSID_TO_ATOM(sprop->id); if (!JS_XDRUint32(xdr, &type) || !JS_XDRUint32(xdr, &userid) || !js_XDRCStringAtom(xdr, &propAtom)) { if (mark) JS_ARENA_RELEASE(&cx->tempPool, mark); goto bad; } } if (mark) JS_ARENA_RELEASE(&cx->tempPool, mark); } else { JSPropertyOp getter, setter; for (i = n; i != 0; i--) { uintN attrs = JSPROP_PERMANENT; if (!JS_XDRUint32(xdr, &type) || !JS_XDRUint32(xdr, &userid) || !js_XDRCStringAtom(xdr, &propAtom)) { goto bad; } JS_ASSERT(type == JSXDR_FUNARG || type == JSXDR_FUNVAR || type == JSXDR_FUNCONST); if (type == JSXDR_FUNARG) { getter = js_GetArgument; setter = js_SetArgument; JS_ASSERT(nargs++ <= fun->nargs); } else if (type == JSXDR_FUNVAR || type == JSXDR_FUNCONST) { getter = js_GetLocalVariable; setter = js_SetLocalVariable; if (type == JSXDR_FUNCONST) attrs |= JSPROP_READONLY; JS_ASSERT(nvars++ <= fun->u.i.nvars); } else { getter = NULL; setter = NULL; } /* Flag duplicate argument if atom is bound in fun->object. */ dupflag = SCOPE_GET_PROPERTY(OBJ_SCOPE(fun->object), ATOM_TO_JSID(propAtom)) ? SPROP_IS_DUPLICATE : 0; if (!js_AddHiddenProperty(cx, fun->object, ATOM_TO_JSID(propAtom), getter, setter, SPROP_INVALID_SLOT, attrs | JSPROP_SHARED, dupflag | SPROP_HAS_SHORTID, JSVAL_TO_INT(userid))) { goto bad; } } } } if (!js_XDRScript(xdr, &fun->u.i.script, NULL)) goto bad; if (xdr->mode == JSXDR_DECODE) { fun->flags = (uint16) flagsword | JSFUN_INTERPRETED; fun->u.i.nregexps = (uint16) (flagsword >> 16); *objp = fun->object; js_CallNewScriptHook(cx, fun->u.i.script, fun); } out: JS_POP_TEMP_ROOT(cx, &tvr); return ok; bad: ok = JS_FALSE; goto out; } #else /* !JS_HAS_XDR */ #define fun_xdrObject NULL #endif /* !JS_HAS_XDR */ /* * [[HasInstance]] internal method for Function objects: fetch the .prototype * property of its 'this' parameter, and walks the prototype chain of v (only * if v is an object) returning true if .prototype is found. */ static JSBool fun_hasInstance(JSContext *cx, JSObject *obj, jsval v, JSBool *bp) { jsval pval; JSString *str; if (!OBJ_GET_PROPERTY(cx, obj, ATOM_TO_JSID(cx->runtime->atomState .classPrototypeAtom), &pval)) { return JS_FALSE; } if (JSVAL_IS_PRIMITIVE(pval)) { /* * Throw a runtime error if instanceof is called on a function that * has a non-object as its .prototype value. */ str = js_DecompileValueGenerator(cx, -1, OBJECT_TO_JSVAL(obj), NULL); if (str) { JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_BAD_PROTOTYPE, JS_GetStringBytes(str)); } return JS_FALSE; } return js_IsDelegate(cx, JSVAL_TO_OBJECT(pval), v, bp); } static uint32 fun_mark(JSContext *cx, JSObject *obj, void *arg) { JSFunction *fun; fun = (JSFunction *) JS_GetPrivate(cx, obj); if (fun) { GC_MARK(cx, fun, "private"); if (fun->atom) GC_MARK_ATOM(cx, fun->atom); if (FUN_INTERPRETED(fun) && fun->u.i.script) js_MarkScript(cx, fun->u.i.script); } return 0; } static uint32 fun_reserveSlots(JSContext *cx, JSObject *obj) { JSFunction *fun; fun = (JSFunction *) JS_GetPrivate(cx, obj); return (fun && FUN_INTERPRETED(fun)) ? fun->u.i.nregexps : 0; } /* * Reserve two slots in all function objects for XPConnect. Note that this * does not bloat every instance, only those on which reserved slots are set, * and those on which ad-hoc properties are defined. */ JS_FRIEND_DATA(JSClass) js_FunctionClass = { js_Function_str, JSCLASS_HAS_PRIVATE | JSCLASS_NEW_RESOLVE | JSCLASS_HAS_RESERVED_SLOTS(2) | JSCLASS_HAS_CACHED_PROTO(JSProto_Function), JS_PropertyStub, JS_PropertyStub, fun_getProperty, JS_PropertyStub, fun_enumerate, (JSResolveOp)fun_resolve, fun_convert, fun_finalize, NULL, NULL, NULL, NULL, fun_xdrObject, fun_hasInstance, fun_mark, fun_reserveSlots }; JSBool js_fun_toString(JSContext *cx, JSObject *obj, uint32 indent, uintN argc, jsval *argv, jsval *rval) { jsval fval; JSFunction *fun; JSString *str; if (!argv) { JS_ASSERT(JS_ObjectIsFunction(cx, obj)); } else { fval = argv[-1]; if (!VALUE_IS_FUNCTION(cx, fval)) { /* * If we don't have a function to start off with, try converting * the object to a function. If that doesn't work, complain. */ if (JSVAL_IS_OBJECT(fval)) { obj = JSVAL_TO_OBJECT(fval); if (!OBJ_GET_CLASS(cx, obj)->convert(cx, obj, JSTYPE_FUNCTION, &fval)) { return JS_FALSE; } argv[-1] = fval; } if (!VALUE_IS_FUNCTION(cx, fval)) { JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_INCOMPATIBLE_PROTO, js_Function_str, js_toString_str, JS_GetTypeName(cx, JS_TypeOfValue(cx, fval))); return JS_FALSE; } } obj = JSVAL_TO_OBJECT(fval); } fun = (JSFunction *) JS_GetPrivate(cx, obj); if (!fun) return JS_TRUE; if (argc && !js_ValueToECMAUint32(cx, argv[0], &indent)) return JS_FALSE; str = JS_DecompileFunction(cx, fun, (uintN)indent); if (!str) return JS_FALSE; *rval = STRING_TO_JSVAL(str); return JS_TRUE; } static JSBool fun_toString(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) { return js_fun_toString(cx, obj, 0, argc, argv, rval); } #if JS_HAS_TOSOURCE static JSBool fun_toSource(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) { return js_fun_toString(cx, obj, JS_DONT_PRETTY_PRINT, argc, argv, rval); } #endif static const char call_str[] = "call"; static JSBool fun_call(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) { jsval fval, *sp, *oldsp; JSString *str; void *mark; uintN i; JSStackFrame *fp; JSBool ok; if (!OBJ_DEFAULT_VALUE(cx, obj, JSTYPE_FUNCTION, &argv[-1])) return JS_FALSE; fval = argv[-1]; if (!VALUE_IS_FUNCTION(cx, fval)) { str = JS_ValueToString(cx, fval); if (str) { JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_INCOMPATIBLE_PROTO, js_Function_str, call_str, JS_GetStringBytes(str)); } return JS_FALSE; } if (argc == 0) { /* Call fun with its global object as the 'this' param if no args. */ obj = NULL; } else { /* Otherwise convert the first arg to 'this' and skip over it. */ if (!js_ValueToObject(cx, argv[0], &obj)) return JS_FALSE; argc--; argv++; } /* Allocate stack space for fval, obj, and the args. */ sp = js_AllocStack(cx, 2 + argc, &mark); if (!sp) return JS_FALSE; /* Push fval, obj, and the args. */ *sp++ = fval; *sp++ = OBJECT_TO_JSVAL(obj); for (i = 0; i < argc; i++) *sp++ = argv[i]; /* Lift current frame to include the args and do the call. */ fp = cx->fp; oldsp = fp->sp; fp->sp = sp; ok = js_Invoke(cx, argc, JSINVOKE_INTERNAL | JSINVOKE_SKIP_CALLER); /* Store rval and pop stack back to our frame's sp. */ *rval = fp->sp[-1]; fp->sp = oldsp; js_FreeStack(cx, mark); return ok; } static JSBool fun_apply(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) { jsval fval, *sp, *oldsp; JSString *str; JSObject *aobj; jsuint length; JSBool arraylike, ok; void *mark; uintN i; JSStackFrame *fp; if (argc == 0) { /* Will get globalObject as 'this' and no other arguments. */ return fun_call(cx, obj, argc, argv, rval); } if (!OBJ_DEFAULT_VALUE(cx, obj, JSTYPE_FUNCTION, &argv[-1])) return JS_FALSE; fval = argv[-1]; if (!VALUE_IS_FUNCTION(cx, fval)) { str = JS_ValueToString(cx, fval); if (str) { JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_INCOMPATIBLE_PROTO, js_Function_str, "apply", JS_GetStringBytes(str)); } return JS_FALSE; } /* Quell GCC overwarnings. */ aobj = NULL; length = 0; if (argc >= 2) { /* If the 2nd arg is null or void, call the function with 0 args. */ if (JSVAL_IS_NULL(argv[1]) || JSVAL_IS_VOID(argv[1])) { argc = 0; } else { /* The second arg must be an array (or arguments object). */ arraylike = JS_FALSE; if (!JSVAL_IS_PRIMITIVE(argv[1])) { aobj = JSVAL_TO_OBJECT(argv[1]); if (!js_IsArrayLike(cx, aobj, &arraylike, &length)) return JS_FALSE; } if (!arraylike) { JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_BAD_APPLY_ARGS, "apply"); return JS_FALSE; } } } /* Convert the first arg to 'this' and skip over it. */ if (!js_ValueToObject(cx, argv[0], &obj)) return JS_FALSE; /* Allocate stack space for fval, obj, and the args. */ argc = (uintN)JS_MIN(length, ARRAY_INIT_LIMIT - 1); sp = js_AllocStack(cx, 2 + argc, &mark); if (!sp) return JS_FALSE; /* Push fval, obj, and aobj's elements as args. */ *sp++ = fval; *sp++ = OBJECT_TO_JSVAL(obj); for (i = 0; i < argc; i++) { ok = JS_GetElement(cx, aobj, (jsint)i, sp); if (!ok) goto out; sp++; } /* Lift current frame to include the args and do the call. */ fp = cx->fp; oldsp = fp->sp; fp->sp = sp; ok = js_Invoke(cx, argc, JSINVOKE_INTERNAL | JSINVOKE_SKIP_CALLER); /* Store rval and pop stack back to our frame's sp. */ *rval = fp->sp[-1]; fp->sp = oldsp; out: js_FreeStack(cx, mark); return ok; } #ifdef NARCISSUS static JSBool fun_applyConstructor(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) { JSObject *aobj; uintN length, i; void *mark; jsval *sp, *newsp, *oldsp; JSStackFrame *fp; JSBool ok; if (JSVAL_IS_PRIMITIVE(argv[0]) || (aobj = JSVAL_TO_OBJECT(argv[0]), OBJ_GET_CLASS(cx, aobj) != &js_ArrayClass && OBJ_GET_CLASS(cx, aobj) != &js_ArgumentsClass)) { JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_BAD_APPLY_ARGS, "__applyConstruct__"); return JS_FALSE; } if (!js_GetLengthProperty(cx, aobj, &length)) return JS_FALSE; if (length >= ARRAY_INIT_LIMIT) length = ARRAY_INIT_LIMIT - 1; newsp = sp = js_AllocStack(cx, 2 + length, &mark); if (!sp) return JS_FALSE; fp = cx->fp; oldsp = fp->sp; *sp++ = OBJECT_TO_JSVAL(obj); *sp++ = JSVAL_NULL; /* This is filled automagically. */ for (i = 0; i < length; i++) { ok = JS_GetElement(cx, aobj, (jsint)i, sp); if (!ok) goto out; sp++; } oldsp = fp->sp; fp->sp = sp; ok = js_InvokeConstructor(cx, newsp, length); *rval = fp->sp[-1]; fp->sp = oldsp; out: js_FreeStack(cx, mark); return ok; } #endif static JSFunctionSpec function_methods[] = { #if JS_HAS_TOSOURCE {js_toSource_str, fun_toSource, 0,0,0}, #endif {js_toString_str, fun_toString, 1,0,0}, {"apply", fun_apply, 2,0,0}, {call_str, fun_call, 1,0,0}, #ifdef NARCISSUS {"__applyConstructor__", fun_applyConstructor, 1,0,0}, #endif {0,0,0,0,0} }; JSBool js_IsIdentifier(JSString *str) { size_t length; jschar c, *chars, *end, *s; length = JSSTRING_LENGTH(str); if (length == 0) return JS_FALSE; chars = JSSTRING_CHARS(str); c = *chars; if (!JS_ISIDSTART(c)) return JS_FALSE; end = chars + length; for (s = chars + 1; s != end; ++s) { c = *s; if (!JS_ISIDENT(c)) return JS_FALSE; } return !js_IsKeyword(chars, length); } static JSBool Function(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) { JSStackFrame *fp, *caller; JSFunction *fun; JSObject *parent; uintN i, n, lineno, dupflag; JSAtom *atom; const char *filename; JSObject *obj2; JSProperty *prop; JSScopeProperty *sprop; JSString *str, *arg; void *mark; JSTokenStream *ts; JSPrincipals *principals; jschar *collected_args, *cp; size_t arg_length, args_length, old_args_length; JSTokenType tt; JSBool ok; fp = cx->fp; if (!(fp->flags & JSFRAME_CONSTRUCTING)) { obj = js_NewObject(cx, &js_FunctionClass, NULL, NULL); if (!obj) return JS_FALSE; *rval = OBJECT_TO_JSVAL(obj); } fun = (JSFunction *) JS_GetPrivate(cx, obj); if (fun) return JS_TRUE; /* * NB: (new Function) is not lexically closed by its caller, it's just an * anonymous function in the top-level scope that its constructor inhabits. * Thus 'var x = 42; f = new Function("return x"); print(f())' prints 42, * and so would a call to f from another top-level's script or function. * * In older versions, before call objects, a new Function was adopted by * its running context's globalObject, which might be different from the * top-level reachable from scopeChain (in HTML frames, e.g.). */ parent = OBJ_GET_PARENT(cx, JSVAL_TO_OBJECT(argv[-2])); fun = js_NewFunction(cx, obj, NULL, 0, JSFUN_LAMBDA, parent, cx->runtime->atomState.anonymousAtom); if (!fun) return JS_FALSE; /* * Function is static and not called directly by other functions in this * file, therefore it is callable only as a native function by js_Invoke. * Find the scripted caller, possibly skipping other native frames such as * are built for Function.prototype.call or .apply activations that invoke * Function indirectly from a script. */ JS_ASSERT(!fp->script && fp->fun && fp->fun->u.n.native == Function); caller = JS_GetScriptedCaller(cx, fp); if (caller) { filename = caller->script->filename; lineno = js_PCToLineNumber(cx, caller->script, caller->pc); principals = JS_EvalFramePrincipals(cx, fp, caller); } else { filename = NULL; lineno = 0; principals = NULL; } /* Belt-and-braces: check that the caller has access to parent. */ if (!js_CheckPrincipalsAccess(cx, parent, principals, CLASS_ATOM(cx, Function))) { return JS_FALSE; } n = argc ? argc - 1 : 0; if (n > 0) { /* * Collect the function-argument arguments into one string, separated * by commas, then make a tokenstream from that string, and scan it to * get the arguments. We need to throw the full scanner at the * problem, because the argument string can legitimately contain * comments and linefeeds. XXX It might be better to concatenate * everything up into a function definition and pass it to the * compiler, but doing it this way is less of a delta from the old * code. See ECMA 15.3.2.1. */ args_length = 0; for (i = 0; i < n; i++) { /* Collect the lengths for all the function-argument arguments. */ arg = js_ValueToString(cx, argv[i]); if (!arg) return JS_FALSE; argv[i] = STRING_TO_JSVAL(arg); /* * Check for overflow. The < test works because the maximum * JSString length fits in 2 fewer bits than size_t has. */ old_args_length = args_length; args_length = old_args_length + JSSTRING_LENGTH(arg); if (args_length < old_args_length) { JS_ReportOutOfMemory(cx); return JS_FALSE; } } /* Add 1 for each joining comma and check for overflow (two ways). */ old_args_length = args_length; args_length = old_args_length + n - 1; if (args_length < old_args_length || args_length >= ~(size_t)0 / sizeof(jschar)) { JS_ReportOutOfMemory(cx); return JS_FALSE; } /* * Allocate a string to hold the concatenated arguments, including room * for a terminating 0. Mark cx->tempPool for later release, to free * collected_args and its tokenstream in one swoop. */ mark = JS_ARENA_MARK(&cx->tempPool); JS_ARENA_ALLOCATE_CAST(cp, jschar *, &cx->tempPool, (args_length+1) * sizeof(jschar)); if (!cp) { JS_ReportOutOfMemory(cx); return JS_FALSE; } collected_args = cp; /* * Concatenate the arguments into the new string, separated by commas. */ for (i = 0; i < n; i++) { arg = JSVAL_TO_STRING(argv[i]); arg_length = JSSTRING_LENGTH(arg); (void) js_strncpy(cp, JSSTRING_CHARS(arg), arg_length); cp += arg_length; /* Add separating comma or terminating 0. */ *cp++ = (i + 1 < n) ? ',' : 0; } /* * Make a tokenstream (allocated from cx->tempPool) that reads from * the given string. */ ts = js_NewTokenStream(cx, collected_args, args_length, filename, lineno, principals); if (!ts) { JS_ARENA_RELEASE(&cx->tempPool, mark); return JS_FALSE; } /* The argument string may be empty or contain no tokens. */ tt = js_GetToken(cx, ts); if (tt != TOK_EOF) { for (;;) { /* * Check that it's a name. This also implicitly guards against * TOK_ERROR, which was already reported. */ if (tt != TOK_NAME) goto bad_formal; /* * Get the atom corresponding to the name from the tokenstream; * we're assured at this point that it's a valid identifier. */ atom = CURRENT_TOKEN(ts).t_atom; if (!js_LookupHiddenProperty(cx, obj, ATOM_TO_JSID(atom), &obj2, &prop)) { goto bad_formal; } sprop = (JSScopeProperty *) prop; dupflag = 0; if (sprop) { ok = JS_TRUE; if (obj2 == obj) { const char *name = js_AtomToPrintableString(cx, atom); /* * A duplicate parameter name. We force a duplicate * node on the SCOPE_LAST_PROP(scope) list with the * same id, distinguished by the SPROP_IS_DUPLICATE * flag, and not mapped by an entry in scope. */ JS_ASSERT(sprop->getter == js_GetArgument); ok = name && js_ReportCompileErrorNumber(cx, ts, JSREPORT_TS | JSREPORT_WARNING | JSREPORT_STRICT, JSMSG_DUPLICATE_FORMAL, name); dupflag = SPROP_IS_DUPLICATE; } OBJ_DROP_PROPERTY(cx, obj2, prop); if (!ok) goto bad_formal; sprop = NULL; } if (!js_AddHiddenProperty(cx, fun->object, ATOM_TO_JSID(atom), js_GetArgument, js_SetArgument, SPROP_INVALID_SLOT, JSPROP_PERMANENT | JSPROP_SHARED, dupflag | SPROP_HAS_SHORTID, fun->nargs)) { goto bad_formal; } if (fun->nargs == JS_BITMASK(16)) { JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_TOO_MANY_FUN_ARGS); goto bad; } fun->nargs++; /* * Get the next token. Stop on end of stream. Otherwise * insist on a comma, get another name, and iterate. */ tt = js_GetToken(cx, ts); if (tt == TOK_EOF) break; if (tt != TOK_COMMA) goto bad_formal; tt = js_GetToken(cx, ts); } } /* Clean up. */ ok = js_CloseTokenStream(cx, ts); JS_ARENA_RELEASE(&cx->tempPool, mark); if (!ok) return JS_FALSE; } if (argc) { str = js_ValueToString(cx, argv[argc-1]); } else { /* Can't use cx->runtime->emptyString because we're called too early. */ str = js_NewStringCopyZ(cx, js_empty_ucstr, 0); } if (!str) return JS_FALSE; if (argv) { /* Use the last arg (or this if argc == 0) as a local GC root. */ argv[(intN)(argc-1)] = STRING_TO_JSVAL(str); } mark = JS_ARENA_MARK(&cx->tempPool); ts = js_NewTokenStream(cx, JSSTRING_CHARS(str), JSSTRING_LENGTH(str), filename, lineno, principals); if (!ts) { ok = JS_FALSE; } else { ok = js_CompileFunctionBody(cx, ts, fun) && js_CloseTokenStream(cx, ts); } JS_ARENA_RELEASE(&cx->tempPool, mark); return ok; bad_formal: /* * Report "malformed formal parameter" iff no illegal char or similar * scanner error was already reported. */ if (!(ts->flags & TSF_ERROR)) JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_BAD_FORMAL); bad: /* * Clean up the arguments string and tokenstream if we failed to parse * the arguments. */ (void)js_CloseTokenStream(cx, ts); JS_ARENA_RELEASE(&cx->tempPool, mark); return JS_FALSE; } JSObject * js_InitFunctionClass(JSContext *cx, JSObject *obj) { JSObject *proto; JSAtom *atom; JSFunction *fun; proto = JS_InitClass(cx, obj, NULL, &js_FunctionClass, Function, 1, function_props, function_methods, NULL, NULL); if (!proto) return NULL; atom = js_Atomize(cx, js_FunctionClass.name, strlen(js_FunctionClass.name), 0); if (!atom) goto bad; fun = js_NewFunction(cx, proto, NULL, 0, 0, obj, NULL); if (!fun) goto bad; fun->u.i.script = js_NewScript(cx, 1, 0, 0); if (!fun->u.i.script) goto bad; fun->u.i.script->code[0] = JSOP_STOP; fun->flags |= JSFUN_INTERPRETED; return proto; bad: cx->weakRoots.newborn[GCX_OBJECT] = NULL; return NULL; } JSObject * js_InitCallClass(JSContext *cx, JSObject *obj) { JSObject *proto; proto = JS_InitClass(cx, obj, NULL, &js_CallClass, NULL, 0, call_props, NULL, NULL, NULL); if (!proto) return NULL; /* * Null Call.prototype's proto slot so that Object.prototype.* does not * pollute the scope of heavyweight functions. */ OBJ_SET_PROTO(cx, proto, NULL); return proto; } JSFunction * js_NewFunction(JSContext *cx, JSObject *funobj, JSNative native, uintN nargs, uintN flags, JSObject *parent, JSAtom *atom) { JSFunction *fun; JSTempValueRooter tvr; /* If funobj is null, allocate an object for it. */ if (funobj) { OBJ_SET_PARENT(cx, funobj, parent); } else { funobj = js_NewObject(cx, &js_FunctionClass, NULL, parent); if (!funobj) return NULL; } /* Protect fun from any potential GC callback. */ JS_PUSH_SINGLE_TEMP_ROOT(cx, OBJECT_TO_JSVAL(funobj), &tvr); /* * Allocate fun after allocating funobj so slot allocation in js_NewObject * does not wipe out fun from newborn[GCX_PRIVATE]. */ fun = (JSFunction *) js_NewGCThing(cx, GCX_PRIVATE, sizeof(JSFunction)); if (!fun) goto out; /* Initialize all function members. */ fun->object = NULL; fun->nargs = nargs; fun->flags = flags & JSFUN_FLAGS_MASK; fun->u.n.native = native; fun->u.n.extra = 0; fun->u.n.spare = 0; fun->atom = atom; fun->clasp = NULL; /* Link fun to funobj and vice versa. */ if (!js_LinkFunctionObject(cx, fun, funobj)) { cx->weakRoots.newborn[GCX_OBJECT] = NULL; fun = NULL; } out: JS_POP_TEMP_ROOT(cx, &tvr); return fun; } JSObject * js_CloneFunctionObject(JSContext *cx, JSObject *funobj, JSObject *parent) { JSObject *newfunobj; JSFunction *fun; JS_ASSERT(OBJ_GET_CLASS(cx, funobj) == &js_FunctionClass); newfunobj = js_NewObject(cx, &js_FunctionClass, funobj, parent); if (!newfunobj) return NULL; fun = (JSFunction *) JS_GetPrivate(cx, funobj); if (!js_LinkFunctionObject(cx, fun, newfunobj)) { cx->weakRoots.newborn[GCX_OBJECT] = NULL; return NULL; } return newfunobj; } JSBool js_LinkFunctionObject(JSContext *cx, JSFunction *fun, JSObject *funobj) { if (!fun->object) fun->object = funobj; return JS_SetPrivate(cx, funobj, fun); } JSFunction * js_DefineFunction(JSContext *cx, JSObject *obj, JSAtom *atom, JSNative native, uintN nargs, uintN attrs) { JSFunction *fun; fun = js_NewFunction(cx, NULL, native, nargs, attrs, obj, atom); if (!fun) return NULL; if (!OBJ_DEFINE_PROPERTY(cx, obj, ATOM_TO_JSID(atom), OBJECT_TO_JSVAL(fun->object), NULL, NULL, attrs & ~JSFUN_FLAGS_MASK, NULL)) { return NULL; } return fun; } #if (JSV2F_CONSTRUCT & JSV2F_SEARCH_STACK) # error "JSINVOKE_CONSTRUCT and JSV2F_SEARCH_STACK are not disjoint!" #endif JSFunction * js_ValueToFunction(JSContext *cx, jsval *vp, uintN flags) { jsval v; JSObject *obj; v = *vp; obj = NULL; if (JSVAL_IS_OBJECT(v)) { obj = JSVAL_TO_OBJECT(v); if (obj && OBJ_GET_CLASS(cx, obj) != &js_FunctionClass) { if (!OBJ_DEFAULT_VALUE(cx, obj, JSTYPE_FUNCTION, &v)) return NULL; obj = VALUE_IS_FUNCTION(cx, v) ? JSVAL_TO_OBJECT(v) : NULL; } } if (!obj) { js_ReportIsNotFunction(cx, vp, flags); return NULL; } return (JSFunction *) JS_GetPrivate(cx, obj); } JSObject * js_ValueToFunctionObject(JSContext *cx, jsval *vp, uintN flags) { JSFunction *fun; JSObject *funobj; JSStackFrame *caller; JSPrincipals *principals; if (VALUE_IS_FUNCTION(cx, *vp)) return JSVAL_TO_OBJECT(*vp); fun = js_ValueToFunction(cx, vp, flags); if (!fun) return NULL; funobj = fun->object; *vp = OBJECT_TO_JSVAL(funobj); caller = JS_GetScriptedCaller(cx, cx->fp); if (caller) { principals = caller->script->principals; } else { /* No scripted caller, don't allow access. */ principals = NULL; } if (!js_CheckPrincipalsAccess(cx, funobj, principals, fun->atom ? fun->atom : cx->runtime->atomState.anonymousAtom)) { return NULL; } return funobj; } JSObject * js_ValueToCallableObject(JSContext *cx, jsval *vp, uintN flags) { JSObject *callable; callable = JSVAL_IS_PRIMITIVE(*vp) ? NULL : JSVAL_TO_OBJECT(*vp); if (callable && ((callable->map->ops == &js_ObjectOps) ? OBJ_GET_CLASS(cx, callable)->call : callable->map->ops->call)) { *vp = OBJECT_TO_JSVAL(callable); } else { callable = js_ValueToFunctionObject(cx, vp, flags); } return callable; } void js_ReportIsNotFunction(JSContext *cx, jsval *vp, uintN flags) { JSStackFrame *fp; JSString *str; JSTempValueRooter tvr; const char *bytes, *source; for (fp = cx->fp; fp && !fp->spbase; fp = fp->down) continue; str = js_DecompileValueGenerator(cx, (fp && fp->spbase <= vp && vp < fp->sp) ? vp - fp->sp : (flags & JSV2F_SEARCH_STACK) ? JSDVG_SEARCH_STACK : JSDVG_IGNORE_STACK, *vp, NULL); if (str) { JS_PUSH_TEMP_ROOT_STRING(cx, str, &tvr); bytes = JS_GetStringBytes(str); if (flags & JSV2F_ITERATOR) { source = js_ValueToPrintableSource(cx, *vp); if (source) { JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_BAD_ITERATOR, bytes, js_iterator_str, source); } } else { JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, (uintN)((flags & JSV2F_CONSTRUCT) ? JSMSG_NOT_CONSTRUCTOR : JSMSG_NOT_FUNCTION), bytes); } JS_POP_TEMP_ROOT(cx, &tvr); } } pacparser-1.4.5/src/spidermonkey/js/src/jsfun.h000066400000000000000000000135171464010763600215200ustar00rootroot00000000000000/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- * * ***** BEGIN LICENSE BLOCK ***** * Version: MPL 1.1/GPL 2.0/LGPL 2.1 * * The contents of this file are subject to the Mozilla Public License Version * 1.1 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * http://www.mozilla.org/MPL/ * * Software distributed under the License is distributed on an "AS IS" basis, * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License * for the specific language governing rights and limitations under the * License. * * The Original Code is Mozilla Communicator client code, released * March 31, 1998. * * The Initial Developer of the Original Code is * Netscape Communications Corporation. * Portions created by the Initial Developer are Copyright (C) 1998 * the Initial Developer. All Rights Reserved. * * Contributor(s): * * Alternatively, the contents of this file may be used under the terms of * either of the GNU General Public License Version 2 or later (the "GPL"), * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), * in which case the provisions of the GPL or the LGPL are applicable instead * of those above. If you wish to allow use of your version of this file only * under the terms of either the GPL or the LGPL, and not to allow others to * use your version of this file under the terms of the MPL, indicate your * decision by deleting the provisions above and replace them with the notice * and other provisions required by the GPL or the LGPL. If you do not delete * the provisions above, a recipient may use your version of this file under * the terms of any one of the MPL, the GPL or the LGPL. * * ***** END LICENSE BLOCK ***** */ #ifndef jsfun_h___ #define jsfun_h___ /* * JS function definitions. */ #include "jsprvtd.h" #include "jspubtd.h" JS_BEGIN_EXTERN_C struct JSFunction { JSObject *object; /* back-pointer to GC'ed object header */ uint16 nargs; /* minimum number of actual arguments */ uint16 flags; /* bound method and other flags, see jsapi.h */ union { struct { uint16 extra; /* number of arg slots for local GC roots */ uint16 spare; /* reserved for future use */ JSNative native; /* native method pointer or null */ } n; struct { uint16 nvars; /* number of local variables */ uint16 nregexps; /* number of regular expressions literals */ JSScript *script; /* interpreted bytecode descriptor or null */ } i; } u; JSAtom *atom; /* name for diagnostics and decompiling */ JSClass *clasp; /* if non-null, constructor for this class */ }; #define JSFUN_INTERPRETED 0x8000 /* use u.i if set, u.n if unset */ #define FUN_INTERPRETED(fun) ((fun)->flags & JSFUN_INTERPRETED) #define FUN_NATIVE(fun) (FUN_INTERPRETED(fun) ? NULL : (fun)->u.n.native) #define FUN_SCRIPT(fun) (FUN_INTERPRETED(fun) ? (fun)->u.i.script : NULL) extern JSClass js_ArgumentsClass; extern JSClass js_CallClass; /* JS_FRIEND_DATA so that VALUE_IS_FUNCTION is callable from the shell. */ extern JS_FRIEND_DATA(JSClass) js_FunctionClass; /* * NB: jsapi.h and jsobj.h must be included before any call to this macro. */ #define VALUE_IS_FUNCTION(cx, v) \ (!JSVAL_IS_PRIMITIVE(v) && \ OBJ_GET_CLASS(cx, JSVAL_TO_OBJECT(v)) == &js_FunctionClass) extern JSBool js_fun_toString(JSContext *cx, JSObject *obj, uint32 indent, uintN argc, jsval *argv, jsval *rval); extern JSBool js_IsIdentifier(JSString *str); extern JSObject * js_InitFunctionClass(JSContext *cx, JSObject *obj); extern JSObject * js_InitArgumentsClass(JSContext *cx, JSObject *obj); extern JSObject * js_InitCallClass(JSContext *cx, JSObject *obj); extern JSFunction * js_NewFunction(JSContext *cx, JSObject *funobj, JSNative native, uintN nargs, uintN flags, JSObject *parent, JSAtom *atom); extern JSObject * js_CloneFunctionObject(JSContext *cx, JSObject *funobj, JSObject *parent); extern JSBool js_LinkFunctionObject(JSContext *cx, JSFunction *fun, JSObject *object); extern JSFunction * js_DefineFunction(JSContext *cx, JSObject *obj, JSAtom *atom, JSNative native, uintN nargs, uintN flags); /* * Flags for js_ValueToFunction and js_ReportIsNotFunction. We depend on the * fact that JSINVOKE_CONSTRUCT (aka JSFRAME_CONSTRUCTING) is 1, and test that * with #if/#error in jsfun.c. */ #define JSV2F_CONSTRUCT JSINVOKE_CONSTRUCT #define JSV2F_ITERATOR JSINVOKE_ITERATOR #define JSV2F_SEARCH_STACK 0x10000 extern JSFunction * js_ValueToFunction(JSContext *cx, jsval *vp, uintN flags); extern JSObject * js_ValueToFunctionObject(JSContext *cx, jsval *vp, uintN flags); extern JSObject * js_ValueToCallableObject(JSContext *cx, jsval *vp, uintN flags); extern void js_ReportIsNotFunction(JSContext *cx, jsval *vp, uintN flags); extern JSObject * js_GetCallObject(JSContext *cx, JSStackFrame *fp, JSObject *parent); extern JSBool js_PutCallObject(JSContext *cx, JSStackFrame *fp); extern JSBool js_GetCallVariable(JSContext *cx, JSObject *obj, jsval id, jsval *vp); extern JSBool js_SetCallVariable(JSContext *cx, JSObject *obj, jsval id, jsval *vp); extern JSBool js_GetArgsValue(JSContext *cx, JSStackFrame *fp, jsval *vp); extern JSBool js_GetArgsProperty(JSContext *cx, JSStackFrame *fp, jsid id, JSObject **objp, jsval *vp); extern JSObject * js_GetArgsObject(JSContext *cx, JSStackFrame *fp); extern JSBool js_PutArgsObject(JSContext *cx, JSStackFrame *fp); extern JSBool js_XDRFunction(JSXDRState *xdr, JSObject **objp); JS_END_EXTERN_C #endif /* jsfun_h___ */ pacparser-1.4.5/src/spidermonkey/js/src/jsgc.c000066400000000000000000003163171464010763600213200ustar00rootroot00000000000000/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- * vim: set ts=8 sw=4 et tw=78: * * ***** BEGIN LICENSE BLOCK ***** * Version: MPL 1.1/GPL 2.0/LGPL 2.1 * * The contents of this file are subject to the Mozilla Public License Version * 1.1 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * http://www.mozilla.org/MPL/ * * Software distributed under the License is distributed on an "AS IS" basis, * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License * for the specific language governing rights and limitations under the * License. * * The Original Code is Mozilla Communicator client code, released * March 31, 1998. * * The Initial Developer of the Original Code is * Netscape Communications Corporation. * Portions created by the Initial Developer are Copyright (C) 1998 * the Initial Developer. All Rights Reserved. * * Contributor(s): * * Alternatively, the contents of this file may be used under the terms of * either of the GNU General Public License Version 2 or later (the "GPL"), * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), * in which case the provisions of the GPL or the LGPL are applicable instead * of those above. If you wish to allow use of your version of this file only * under the terms of either the GPL or the LGPL, and not to allow others to * use your version of this file under the terms of the MPL, indicate your * decision by deleting the provisions above and replace them with the notice * and other provisions required by the GPL or the LGPL. If you do not delete * the provisions above, a recipient may use your version of this file under * the terms of any one of the MPL, the GPL or the LGPL. * * ***** END LICENSE BLOCK ***** */ /* * JS Mark-and-Sweep Garbage Collector. * * This GC allocates fixed-sized things with sizes up to GC_NBYTES_MAX (see * jsgc.h). It allocates from a special GC arena pool with each arena allocated * using malloc. It uses an ideally parallel array of flag bytes to hold the * mark bit, finalizer type index, etc. * * XXX swizzle page to freelist for better locality of reference */ #include "jsstddef.h" #include /* for free */ #include /* for memset used when DEBUG */ #include "jstypes.h" #include "jsutil.h" /* Added by JSIFY */ #include "jshash.h" /* Added by JSIFY */ #include "jsapi.h" #include "jsatom.h" #include "jsbit.h" #include "jsclist.h" #include "jscntxt.h" #include "jsconfig.h" #include "jsdbgapi.h" #include "jsexn.h" #include "jsfun.h" #include "jsgc.h" #include "jsinterp.h" #include "jsiter.h" #include "jslock.h" #include "jsnum.h" #include "jsobj.h" #include "jsscope.h" #include "jsscript.h" #include "jsstr.h" #if JS_HAS_XML_SUPPORT #include "jsxml.h" #endif /* * GC arena sizing depends on amortizing arena overhead using a large number * of things per arena, and on the thing/flags ratio of 8:1 on most platforms. * * On 64-bit platforms, we would have half as many things per arena because * pointers are twice as big, so we double the bytes for things per arena. * This preserves the 1024 byte flags sub-arena size, which relates to the * GC_PAGE_SIZE (see below for why). */ #if JS_BYTES_PER_WORD == 8 # define GC_THINGS_SHIFT 14 /* 16KB for things on Alpha, etc. */ #else # define GC_THINGS_SHIFT 13 /* 8KB for things on most platforms */ #endif #define GC_THINGS_SIZE JS_BIT(GC_THINGS_SHIFT) #define GC_FLAGS_SIZE (GC_THINGS_SIZE / sizeof(JSGCThing)) /* * A GC arena contains one flag byte for each thing in its heap, and supports * O(1) lookup of a flag given its thing's address. * * To implement this, we take advantage of the thing/flags numerology: given * the 8K bytes worth of GC-things, there are 1K flag bytes. Within each 9K * allocation for things+flags there are always 8 consecutive 1K-pages each * aligned on 1K boundary. We use these pages to allocate things and the * remaining 1K of space before and after the aligned pages to store flags. * If we are really lucky and things+flags starts on a 1K boundary, then * flags would consist of a single 1K chunk that comes after 8K of things. * Otherwise there are 2 chunks of flags, one before and one after things. * * To be able to find the flag byte for a particular thing, we put a * JSGCPageInfo record at the beginning of each 1K-aligned page to hold that * page's offset from the beginning of things+flags allocation and we allocate * things after this record. Thus for each thing |thing_address & ~1023| * gives the address of a JSGCPageInfo record from which we read page_offset. * Due to page alignment * (page_offset & ~1023) + (thing_address & 1023) * gives thing_offset from the beginning of 8K paged things. We then divide * thing_offset by sizeof(JSGCThing) to get thing_index. * * Now |page_address - page_offset| is things+flags arena_address and * (page_offset & 1023) is the offset of the first page from the start of * things+flags area. Thus if * thing_index < (page_offset & 1023) * then * allocation_start_address + thing_index < address_of_the_first_page * and we use * allocation_start_address + thing_index * as the address to store thing's flags. If * thing_index >= (page_offset & 1023), * then we use the chunk of flags that comes after the pages with things * and calculate the address for the flag byte as * address_of_the_first_page + 8K + (thing_index - (page_offset & 1023)) * which is just * allocation_start_address + thing_index + 8K. * * When we allocate things with size equal to sizeof(JSGCThing), the overhead * of this scheme for 32 bit platforms is (8+8*(8+1))/(8+9K) or 0.87% * (assuming 4 bytes for each JSGCArena header, and 8 bytes for each * JSGCThing and JSGCPageInfo). When thing_size > 8, the scheme wastes the * flag byte for each extra 8 bytes beyond sizeof(JSGCThing) in thing_size * and the overhead is close to 1/8 or 12.5%. * FIXME: How can we avoid this overhead? * * Here's some ASCII art showing an arena: * * split or the first 1-K aligned address. * | * V * +--+-------+-------+-------+-------+-------+-------+-------+-------+-----+ * |fB| tp0 | tp1 | tp2 | tp3 | tp4 | tp5 | tp6 | tp7 | fA | * +--+-------+-------+-------+-------+-------+-------+-------+-------+-----+ * ^ ^ * tI ---------+ | * tJ -------------------------------------------+ * * - fB are the "before split" flags, fA are the "after split" flags * - tp0-tp7 are the 8 thing pages * - thing tI points into tp1, whose flags are below the split, in fB * - thing tJ points into tp5, clearly above the split * * In general, one of the thing pages will have some of its things' flags on * the low side of the split, and the rest of its things' flags on the high * side. All the other pages have flags only below or only above. * * (If we need to implement card-marking for an incremental GC write barrier, * we can replace word-sized offsetInArena in JSGCPageInfo by pair of * uint8 card_mark and uint16 offsetInArena fields as the offset can not exceed * GC_THINGS_SIZE. This would gives an extremely efficient write barrier: * when mutating an object obj, just store a 1 byte at * (uint8 *) ((jsuword)obj & ~1023) on 32-bit platforms.) */ #define GC_PAGE_SHIFT 10 #define GC_PAGE_MASK ((jsuword) JS_BITMASK(GC_PAGE_SHIFT)) #define GC_PAGE_SIZE JS_BIT(GC_PAGE_SHIFT) #define GC_PAGE_COUNT (1 << (GC_THINGS_SHIFT - GC_PAGE_SHIFT)) typedef struct JSGCPageInfo { jsuword offsetInArena; /* offset from the arena start */ jsuword unscannedBitmap; /* bitset for fast search of marked but not yet scanned GC things */ } JSGCPageInfo; struct JSGCArena { JSGCArenaList *list; /* allocation list for the arena */ JSGCArena *prev; /* link field for allocation list */ JSGCArena *prevUnscanned; /* link field for the list of arenas with marked but not yet scanned things */ jsuword unscannedPages; /* bitset for fast search of pages with marked but not yet scanned things */ uint8 base[1]; /* things+flags allocation area */ }; #define GC_ARENA_SIZE \ (offsetof(JSGCArena, base) + GC_THINGS_SIZE + GC_FLAGS_SIZE) #define FIRST_THING_PAGE(a) \ (((jsuword)(a)->base + GC_FLAGS_SIZE - 1) & ~GC_PAGE_MASK) #define PAGE_TO_ARENA(pi) \ ((JSGCArena *)((jsuword)(pi) - (pi)->offsetInArena \ - offsetof(JSGCArena, base))) #define PAGE_INDEX(pi) \ ((size_t)((pi)->offsetInArena >> GC_PAGE_SHIFT)) #define THING_TO_PAGE(thing) \ ((JSGCPageInfo *)((jsuword)(thing) & ~GC_PAGE_MASK)) /* * Given a thing size n, return the size of the gap from the page start before * the first thing. We know that any n not a power of two packs from * the end of the page leaving at least enough room for one JSGCPageInfo, but * not for another thing, at the front of the page (JS_ASSERTs below insist * on this). * * This works because all allocations are a multiple of sizeof(JSGCThing) == * sizeof(JSGCPageInfo) in size. */ #define PAGE_THING_GAP(n) (((n) & ((n) - 1)) ? (GC_PAGE_SIZE % (n)) : (n)) #ifdef JS_THREADSAFE /* * The maximum number of things to put to the local free list by taking * several things from the global free list or from the tail of the last * allocated arena to amortize the cost of rt->gcLock. * * We use number 8 based on benchmarks from bug 312238. */ #define MAX_THREAD_LOCAL_THINGS 8 #endif JS_STATIC_ASSERT(sizeof(JSGCThing) == sizeof(JSGCPageInfo)); JS_STATIC_ASSERT(sizeof(JSGCThing) >= sizeof(JSObject)); JS_STATIC_ASSERT(sizeof(JSGCThing) >= sizeof(JSString)); JS_STATIC_ASSERT(sizeof(JSGCThing) >= sizeof(jsdouble)); JS_STATIC_ASSERT(GC_FLAGS_SIZE >= GC_PAGE_SIZE); JS_STATIC_ASSERT(sizeof(JSStackHeader) >= 2 * sizeof(jsval)); /* * JSPtrTable capacity growth descriptor. The table grows by powers of two * starting from capacity JSPtrTableInfo.minCapacity, but switching to linear * growth when capacity reaches JSPtrTableInfo.linearGrowthThreshold. */ typedef struct JSPtrTableInfo { uint16 minCapacity; uint16 linearGrowthThreshold; } JSPtrTableInfo; #define GC_ITERATOR_TABLE_MIN 4 #define GC_ITERATOR_TABLE_LINEAR 1024 static const JSPtrTableInfo iteratorTableInfo = { GC_ITERATOR_TABLE_MIN, GC_ITERATOR_TABLE_LINEAR }; /* Calculate table capacity based on the current value of JSPtrTable.count. */ static size_t PtrTableCapacity(size_t count, const JSPtrTableInfo *info) { size_t linear, log, capacity; linear = info->linearGrowthThreshold; JS_ASSERT(info->minCapacity <= linear); if (count == 0) { capacity = 0; } else if (count < linear) { log = JS_CEILING_LOG2W(count); JS_ASSERT(log != JS_BITS_PER_WORD); capacity = (size_t)1 << log; if (capacity < info->minCapacity) capacity = info->minCapacity; } else { capacity = JS_ROUNDUP(count, linear); } JS_ASSERT(capacity >= count); return capacity; } static void FreePtrTable(JSPtrTable *table, const JSPtrTableInfo *info) { if (table->array) { JS_ASSERT(table->count > 0); free(table->array); table->array = NULL; table->count = 0; } JS_ASSERT(table->count == 0); } static JSBool AddToPtrTable(JSContext *cx, JSPtrTable *table, const JSPtrTableInfo *info, void *ptr) { size_t count, capacity; void **array; count = table->count; capacity = PtrTableCapacity(count, info); if (count == capacity) { if (capacity < info->minCapacity) { JS_ASSERT(capacity == 0); JS_ASSERT(!table->array); capacity = info->minCapacity; } else { /* * Simplify the overflow detection assuming pointer is bigger * than byte. */ JS_STATIC_ASSERT(2 <= sizeof table->array[0]); capacity = (capacity < info->linearGrowthThreshold) ? 2 * capacity : capacity + info->linearGrowthThreshold; if (capacity > (size_t)-1 / sizeof table->array[0]) goto bad; } array = (void **) realloc(table->array, capacity * sizeof table->array[0]); if (!array) goto bad; #ifdef DEBUG memset(array + count, JS_FREE_PATTERN, (capacity - count) * sizeof table->array[0]); #endif table->array = array; } table->array[count] = ptr; table->count = count + 1; return JS_TRUE; bad: JS_ReportOutOfMemory(cx); return JS_FALSE; } static void ShrinkPtrTable(JSPtrTable *table, const JSPtrTableInfo *info, size_t newCount) { size_t oldCapacity, capacity; void **array; JS_ASSERT(newCount <= table->count); if (newCount == table->count) return; oldCapacity = PtrTableCapacity(table->count, info); table->count = newCount; capacity = PtrTableCapacity(newCount, info); if (oldCapacity != capacity) { array = table->array; JS_ASSERT(array); if (capacity == 0) { free(array); table->array = NULL; return; } array = (void **) realloc(array, capacity * sizeof array[0]); if (array) table->array = array; } #ifdef DEBUG memset(table->array + newCount, JS_FREE_PATTERN, (capacity - newCount) * sizeof table->array[0]); #endif } #ifdef JS_GCMETER # define METER(x) x #else # define METER(x) ((void) 0) #endif static JSBool NewGCArena(JSRuntime *rt, JSGCArenaList *arenaList) { JSGCArena *a; jsuword offset; JSGCPageInfo *pi; uint32 *bytesptr; /* Check if we are allowed and can allocate a new arena. */ if (rt->gcBytes >= rt->gcMaxBytes) return JS_FALSE; a = (JSGCArena *)malloc(GC_ARENA_SIZE); if (!a) return JS_FALSE; /* Initialize the JSGCPageInfo records at the start of every thing page. */ offset = (GC_PAGE_SIZE - ((jsuword)a->base & GC_PAGE_MASK)) & GC_PAGE_MASK; JS_ASSERT((jsuword)a->base + offset == FIRST_THING_PAGE(a)); do { pi = (JSGCPageInfo *) (a->base + offset); pi->offsetInArena = offset; pi->unscannedBitmap = 0; offset += GC_PAGE_SIZE; } while (offset < GC_THINGS_SIZE); METER(++arenaList->stats.narenas); METER(arenaList->stats.maxarenas = JS_MAX(arenaList->stats.maxarenas, arenaList->stats.narenas)); a->list = arenaList; a->prev = arenaList->last; a->prevUnscanned = NULL; a->unscannedPages = 0; arenaList->last = a; arenaList->lastLimit = 0; bytesptr = (arenaList == &rt->gcArenaList[0]) ? &rt->gcBytes : &rt->gcPrivateBytes; *bytesptr += GC_ARENA_SIZE; return JS_TRUE; } static void DestroyGCArena(JSRuntime *rt, JSGCArenaList *arenaList, JSGCArena **ap) { JSGCArena *a; uint32 *bytesptr; a = *ap; JS_ASSERT(a); bytesptr = (arenaList == &rt->gcArenaList[0]) ? &rt->gcBytes : &rt->gcPrivateBytes; JS_ASSERT(*bytesptr >= GC_ARENA_SIZE); *bytesptr -= GC_ARENA_SIZE; METER(rt->gcStats.afree++); METER(--arenaList->stats.narenas); if (a == arenaList->last) arenaList->lastLimit = (uint16)(a->prev ? GC_THINGS_SIZE : 0); *ap = a->prev; #ifdef DEBUG memset(a, JS_FREE_PATTERN, GC_ARENA_SIZE); #endif free(a); } static void InitGCArenaLists(JSRuntime *rt) { uintN i, thingSize; JSGCArenaList *arenaList; for (i = 0; i < GC_NUM_FREELISTS; i++) { arenaList = &rt->gcArenaList[i]; thingSize = GC_FREELIST_NBYTES(i); JS_ASSERT((size_t)(uint16)thingSize == thingSize); arenaList->last = NULL; arenaList->lastLimit = 0; arenaList->thingSize = (uint16)thingSize; arenaList->freeList = NULL; METER(memset(&arenaList->stats, 0, sizeof arenaList->stats)); } } static void FinishGCArenaLists(JSRuntime *rt) { uintN i; JSGCArenaList *arenaList; for (i = 0; i < GC_NUM_FREELISTS; i++) { arenaList = &rt->gcArenaList[i]; while (arenaList->last) DestroyGCArena(rt, arenaList, &arenaList->last); arenaList->freeList = NULL; } } uint8 * js_GetGCThingFlags(void *thing) { JSGCPageInfo *pi; jsuword offsetInArena, thingIndex; pi = THING_TO_PAGE(thing); offsetInArena = pi->offsetInArena; JS_ASSERT(offsetInArena < GC_THINGS_SIZE); thingIndex = ((offsetInArena & ~GC_PAGE_MASK) | ((jsuword)thing & GC_PAGE_MASK)) / sizeof(JSGCThing); JS_ASSERT(thingIndex < GC_PAGE_SIZE); if (thingIndex >= (offsetInArena & GC_PAGE_MASK)) thingIndex += GC_THINGS_SIZE; return (uint8 *)pi - offsetInArena + thingIndex; } JSRuntime* js_GetGCStringRuntime(JSString *str) { JSGCPageInfo *pi; JSGCArenaList *list; pi = THING_TO_PAGE(str); list = PAGE_TO_ARENA(pi)->list; JS_ASSERT(list->thingSize == sizeof(JSGCThing)); JS_ASSERT(GC_FREELIST_INDEX(sizeof(JSGCThing)) == 0); return (JSRuntime *)((uint8 *)list - offsetof(JSRuntime, gcArenaList)); } JSBool js_IsAboutToBeFinalized(JSContext *cx, void *thing) { uint8 flags = *js_GetGCThingFlags(thing); return !(flags & (GCF_MARK | GCF_LOCK | GCF_FINAL)); } typedef void (*GCFinalizeOp)(JSContext *cx, JSGCThing *thing); #ifndef DEBUG # define js_FinalizeDouble NULL #endif #if !JS_HAS_XML_SUPPORT # define js_FinalizeXMLNamespace NULL # define js_FinalizeXMLQName NULL # define js_FinalizeXML NULL #endif static GCFinalizeOp gc_finalizers[GCX_NTYPES] = { (GCFinalizeOp) js_FinalizeObject, /* GCX_OBJECT */ (GCFinalizeOp) js_FinalizeString, /* GCX_STRING */ (GCFinalizeOp) js_FinalizeDouble, /* GCX_DOUBLE */ (GCFinalizeOp) js_FinalizeString, /* GCX_MUTABLE_STRING */ NULL, /* GCX_PRIVATE */ (GCFinalizeOp) js_FinalizeXMLNamespace, /* GCX_NAMESPACE */ (GCFinalizeOp) js_FinalizeXMLQName, /* GCX_QNAME */ (GCFinalizeOp) js_FinalizeXML, /* GCX_XML */ NULL, /* GCX_EXTERNAL_STRING */ NULL, NULL, NULL, NULL, NULL, NULL, NULL }; #ifdef GC_MARK_DEBUG static const char newborn_external_string[] = "newborn external string"; static const char *gc_typenames[GCX_NTYPES] = { "newborn object", "newborn string", "newborn double", "newborn mutable string", "newborn private", "newborn Namespace", "newborn QName", "newborn XML", newborn_external_string, newborn_external_string, newborn_external_string, newborn_external_string, newborn_external_string, newborn_external_string, newborn_external_string, newborn_external_string }; #endif intN js_ChangeExternalStringFinalizer(JSStringFinalizeOp oldop, JSStringFinalizeOp newop) { uintN i; for (i = GCX_EXTERNAL_STRING; i < GCX_NTYPES; i++) { if (gc_finalizers[i] == (GCFinalizeOp) oldop) { gc_finalizers[i] = (GCFinalizeOp) newop; return (intN) i; } } return -1; } /* This is compatible with JSDHashEntryStub. */ typedef struct JSGCRootHashEntry { JSDHashEntryHdr hdr; void *root; const char *name; } JSGCRootHashEntry; /* Initial size of the gcRootsHash table (SWAG, small enough to amortize). */ #define GC_ROOTS_SIZE 256 #define GC_FINALIZE_LEN 1024 JSBool js_InitGC(JSRuntime *rt, uint32 maxbytes) { InitGCArenaLists(rt); if (!JS_DHashTableInit(&rt->gcRootsHash, JS_DHashGetStubOps(), NULL, sizeof(JSGCRootHashEntry), GC_ROOTS_SIZE)) { rt->gcRootsHash.ops = NULL; return JS_FALSE; } rt->gcLocksHash = NULL; /* create lazily */ /* * Separate gcMaxMallocBytes from gcMaxBytes but initialize to maxbytes * for default backward API compatibility. */ rt->gcMaxBytes = rt->gcMaxMallocBytes = maxbytes; return JS_TRUE; } #ifdef JS_GCMETER JS_FRIEND_API(void) js_DumpGCStats(JSRuntime *rt, FILE *fp) { uintN i; size_t totalThings, totalMaxThings, totalBytes; fprintf(fp, "\nGC allocation statistics:\n"); #define UL(x) ((unsigned long)(x)) #define ULSTAT(x) UL(rt->gcStats.x) totalThings = 0; totalMaxThings = 0; totalBytes = 0; for (i = 0; i < GC_NUM_FREELISTS; i++) { JSGCArenaList *list = &rt->gcArenaList[i]; JSGCArenaStats *stats = &list->stats; if (stats->maxarenas == 0) { fprintf(fp, "ARENA LIST %u (thing size %lu): NEVER USED\n", i, UL(GC_FREELIST_NBYTES(i))); continue; } fprintf(fp, "ARENA LIST %u (thing size %lu):\n", i, UL(GC_FREELIST_NBYTES(i))); fprintf(fp, " arenas: %lu\n", UL(stats->narenas)); fprintf(fp, " max arenas: %lu\n", UL(stats->maxarenas)); fprintf(fp, " things: %lu\n", UL(stats->nthings)); fprintf(fp, " max things: %lu\n", UL(stats->maxthings)); fprintf(fp, " free list: %lu\n", UL(stats->freelen)); fprintf(fp, " free list density: %.1f%%\n", stats->narenas == 0 ? 0.0 : (100.0 * list->thingSize * (jsdouble)stats->freelen / (GC_THINGS_SIZE * (jsdouble)stats->narenas))); fprintf(fp, " average free list density: %.1f%%\n", stats->totalarenas == 0 ? 0.0 : (100.0 * list->thingSize * (jsdouble)stats->totalfreelen / (GC_THINGS_SIZE * (jsdouble)stats->totalarenas))); fprintf(fp, " recycles: %lu\n", UL(stats->recycle)); fprintf(fp, " recycle/alloc ratio: %.2f\n", (jsdouble)stats->recycle / (jsdouble)(stats->totalnew - stats->recycle)); totalThings += stats->nthings; totalMaxThings += stats->maxthings; totalBytes += GC_FREELIST_NBYTES(i) * stats->nthings; } fprintf(fp, "TOTAL STATS:\n"); fprintf(fp, " public bytes allocated: %lu\n", UL(rt->gcBytes)); fprintf(fp, " private bytes allocated: %lu\n", UL(rt->gcPrivateBytes)); fprintf(fp, " alloc attempts: %lu\n", ULSTAT(alloc)); #ifdef JS_THREADSAFE fprintf(fp, " alloc without locks: %1u\n", ULSTAT(localalloc)); #endif fprintf(fp, " total GC things: %lu\n", UL(totalThings)); fprintf(fp, " max total GC things: %lu\n", UL(totalMaxThings)); fprintf(fp, " GC things size: %lu\n", UL(totalBytes)); fprintf(fp, "allocation retries after GC: %lu\n", ULSTAT(retry)); fprintf(fp, " allocation failures: %lu\n", ULSTAT(fail)); fprintf(fp, " things born locked: %lu\n", ULSTAT(lockborn)); fprintf(fp, " valid lock calls: %lu\n", ULSTAT(lock)); fprintf(fp, " valid unlock calls: %lu\n", ULSTAT(unlock)); fprintf(fp, " mark recursion depth: %lu\n", ULSTAT(depth)); fprintf(fp, " maximum mark recursion: %lu\n", ULSTAT(maxdepth)); fprintf(fp, " mark C recursion depth: %lu\n", ULSTAT(cdepth)); fprintf(fp, " maximum mark C recursion: %lu\n", ULSTAT(maxcdepth)); fprintf(fp, " delayed scan bag adds: %lu\n", ULSTAT(unscanned)); #ifdef DEBUG fprintf(fp, " max delayed scan bag size: %lu\n", ULSTAT(maxunscanned)); #endif fprintf(fp, " maximum GC nesting level: %lu\n", ULSTAT(maxlevel)); fprintf(fp, "potentially useful GC calls: %lu\n", ULSTAT(poke)); fprintf(fp, " useless GC calls: %lu\n", ULSTAT(nopoke)); fprintf(fp, " thing arenas freed so far: %lu\n", ULSTAT(afree)); fprintf(fp, " stack segments scanned: %lu\n", ULSTAT(stackseg)); fprintf(fp, "stack segment slots scanned: %lu\n", ULSTAT(segslots)); fprintf(fp, "reachable closeable objects: %lu\n", ULSTAT(nclose)); fprintf(fp, " max reachable closeable: %lu\n", ULSTAT(maxnclose)); fprintf(fp, " scheduled close hooks: %lu\n", ULSTAT(closelater)); fprintf(fp, " max scheduled close hooks: %lu\n", ULSTAT(maxcloselater)); #undef UL #undef US #ifdef JS_ARENAMETER JS_DumpArenaStats(fp); #endif } #endif #ifdef DEBUG static void CheckLeakedRoots(JSRuntime *rt); #endif void js_FinishGC(JSRuntime *rt) { #ifdef JS_ARENAMETER JS_DumpArenaStats(stdout); #endif #ifdef JS_GCMETER js_DumpGCStats(rt, stdout); #endif FreePtrTable(&rt->gcIteratorTable, &iteratorTableInfo); #if JS_HAS_GENERATORS rt->gcCloseState.reachableList = NULL; METER(rt->gcStats.nclose = 0); rt->gcCloseState.todoQueue = NULL; #endif FinishGCArenaLists(rt); if (rt->gcRootsHash.ops) { #ifdef DEBUG CheckLeakedRoots(rt); #endif JS_DHashTableFinish(&rt->gcRootsHash); rt->gcRootsHash.ops = NULL; } if (rt->gcLocksHash) { JS_DHashTableDestroy(rt->gcLocksHash); rt->gcLocksHash = NULL; } } JSBool js_AddRoot(JSContext *cx, void *rp, const char *name) { JSBool ok = js_AddRootRT(cx->runtime, rp, name); if (!ok) JS_ReportOutOfMemory(cx); return ok; } JSBool js_AddRootRT(JSRuntime *rt, void *rp, const char *name) { JSBool ok; JSGCRootHashEntry *rhe; /* * Due to the long-standing, but now removed, use of rt->gcLock across the * bulk of js_GC, API users have come to depend on JS_AddRoot etc. locking * properly with a racing GC, without calling JS_AddRoot from a request. * We have to preserve API compatibility here, now that we avoid holding * rt->gcLock across the mark phase (including the root hashtable mark). * * If the GC is running and we're called on another thread, wait for this * GC activation to finish. We can safely wait here (in the case where we * are called within a request on another thread's context) without fear * of deadlock because the GC doesn't set rt->gcRunning until after it has * waited for all active requests to end. */ JS_LOCK_GC(rt); #ifdef JS_THREADSAFE JS_ASSERT(!rt->gcRunning || rt->gcLevel > 0); if (rt->gcRunning && rt->gcThread->id != js_CurrentThreadId()) { do { JS_AWAIT_GC_DONE(rt); } while (rt->gcLevel > 0); } #endif rhe = (JSGCRootHashEntry *) JS_DHashTableOperate(&rt->gcRootsHash, rp, JS_DHASH_ADD); if (rhe) { rhe->root = rp; rhe->name = name; ok = JS_TRUE; } else { ok = JS_FALSE; } JS_UNLOCK_GC(rt); return ok; } JSBool js_RemoveRoot(JSRuntime *rt, void *rp) { /* * Due to the JS_RemoveRootRT API, we may be called outside of a request. * Same synchronization drill as above in js_AddRoot. */ JS_LOCK_GC(rt); #ifdef JS_THREADSAFE JS_ASSERT(!rt->gcRunning || rt->gcLevel > 0); if (rt->gcRunning && rt->gcThread->id != js_CurrentThreadId()) { do { JS_AWAIT_GC_DONE(rt); } while (rt->gcLevel > 0); } #endif (void) JS_DHashTableOperate(&rt->gcRootsHash, rp, JS_DHASH_REMOVE); rt->gcPoke = JS_TRUE; JS_UNLOCK_GC(rt); return JS_TRUE; } #ifdef DEBUG JS_STATIC_DLL_CALLBACK(JSDHashOperator) js_root_printer(JSDHashTable *table, JSDHashEntryHdr *hdr, uint32 i, void *arg) { uint32 *leakedroots = (uint32 *)arg; JSGCRootHashEntry *rhe = (JSGCRootHashEntry *)hdr; (*leakedroots)++; fprintf(stderr, "JS engine warning: leaking GC root \'%s\' at %p\n", rhe->name ? (char *)rhe->name : "", rhe->root); return JS_DHASH_NEXT; } static void CheckLeakedRoots(JSRuntime *rt) { uint32 leakedroots = 0; /* Warn (but don't assert) debug builds of any remaining roots. */ JS_DHashTableEnumerate(&rt->gcRootsHash, js_root_printer, &leakedroots); if (leakedroots > 0) { if (leakedroots == 1) { fprintf(stderr, "JS engine warning: 1 GC root remains after destroying the JSRuntime.\n" " This root may point to freed memory. Objects reachable\n" " through it have not been finalized.\n"); } else { fprintf(stderr, "JS engine warning: %lu GC roots remain after destroying the JSRuntime.\n" " These roots may point to freed memory. Objects reachable\n" " through them have not been finalized.\n", (unsigned long) leakedroots); } } } typedef struct NamedRootDumpArgs { void (*dump)(const char *name, void *rp, void *data); void *data; } NamedRootDumpArgs; JS_STATIC_DLL_CALLBACK(JSDHashOperator) js_named_root_dumper(JSDHashTable *table, JSDHashEntryHdr *hdr, uint32 number, void *arg) { NamedRootDumpArgs *args = (NamedRootDumpArgs *) arg; JSGCRootHashEntry *rhe = (JSGCRootHashEntry *)hdr; if (rhe->name) args->dump(rhe->name, rhe->root, args->data); return JS_DHASH_NEXT; } void js_DumpNamedRoots(JSRuntime *rt, void (*dump)(const char *name, void *rp, void *data), void *data) { NamedRootDumpArgs args; args.dump = dump; args.data = data; JS_DHashTableEnumerate(&rt->gcRootsHash, js_named_root_dumper, &args); } #endif /* DEBUG */ typedef struct GCRootMapArgs { JSGCRootMapFun map; void *data; } GCRootMapArgs; JS_STATIC_DLL_CALLBACK(JSDHashOperator) js_gcroot_mapper(JSDHashTable *table, JSDHashEntryHdr *hdr, uint32 number, void *arg) { GCRootMapArgs *args = (GCRootMapArgs *) arg; JSGCRootHashEntry *rhe = (JSGCRootHashEntry *)hdr; intN mapflags; JSDHashOperator op; mapflags = args->map(rhe->root, rhe->name, args->data); #if JS_MAP_GCROOT_NEXT == JS_DHASH_NEXT && \ JS_MAP_GCROOT_STOP == JS_DHASH_STOP && \ JS_MAP_GCROOT_REMOVE == JS_DHASH_REMOVE op = (JSDHashOperator)mapflags; #else op = JS_DHASH_NEXT; if (mapflags & JS_MAP_GCROOT_STOP) op |= JS_DHASH_STOP; if (mapflags & JS_MAP_GCROOT_REMOVE) op |= JS_DHASH_REMOVE; #endif return op; } uint32 js_MapGCRoots(JSRuntime *rt, JSGCRootMapFun map, void *data) { GCRootMapArgs args; uint32 rv; args.map = map; args.data = data; JS_LOCK_GC(rt); rv = JS_DHashTableEnumerate(&rt->gcRootsHash, js_gcroot_mapper, &args); JS_UNLOCK_GC(rt); return rv; } JSBool js_RegisterCloseableIterator(JSContext *cx, JSObject *obj) { JSRuntime *rt; JSBool ok; rt = cx->runtime; JS_ASSERT(!rt->gcRunning); JS_LOCK_GC(rt); ok = AddToPtrTable(cx, &rt->gcIteratorTable, &iteratorTableInfo, obj); JS_UNLOCK_GC(rt); return ok; } static void CloseIteratorStates(JSContext *cx) { JSRuntime *rt; size_t count, newCount, i; void **array; JSObject *obj; rt = cx->runtime; count = rt->gcIteratorTable.count; array = rt->gcIteratorTable.array; newCount = 0; for (i = 0; i != count; ++i) { obj = (JSObject *)array[i]; if (js_IsAboutToBeFinalized(cx, obj)) js_CloseIteratorState(cx, obj); else array[newCount++] = obj; } ShrinkPtrTable(&rt->gcIteratorTable, &iteratorTableInfo, newCount); } #if JS_HAS_GENERATORS void js_RegisterGenerator(JSContext *cx, JSGenerator *gen) { JSRuntime *rt; rt = cx->runtime; JS_ASSERT(!rt->gcRunning); JS_ASSERT(rt->state != JSRTS_LANDING); JS_ASSERT(gen->state == JSGEN_NEWBORN); JS_LOCK_GC(rt); gen->next = rt->gcCloseState.reachableList; rt->gcCloseState.reachableList = gen; METER(rt->gcStats.nclose++); METER(rt->gcStats.maxnclose = JS_MAX(rt->gcStats.maxnclose, rt->gcStats.nclose)); JS_UNLOCK_GC(rt); } /* * We do not run close hooks when the parent scope of the generator instance * becomes unreachable to prevent denial-of-service and resource leakage from * misbehaved generators. * * Called from the GC. */ static JSBool CanScheduleCloseHook(JSGenerator *gen) { JSObject *parent; JSBool canSchedule; /* Avoid OBJ_GET_PARENT overhead as we are in GC. */ parent = JSVAL_TO_OBJECT(gen->obj->slots[JSSLOT_PARENT]); canSchedule = *js_GetGCThingFlags(parent) & GCF_MARK; #ifdef DEBUG_igor if (!canSchedule) { fprintf(stderr, "GEN: Kill without schedule, gen=%p parent=%p\n", (void *)gen, (void *)parent); } #endif return canSchedule; } /* * Check if we should delay execution of the close hook. * * Called outside GC or any locks. * * XXX The current implementation is a hack that embeds the knowledge of the * browser embedding pending the resolution of bug 352788. In the browser we * must not close any generators that came from a page that is currently in * the browser history. We detect that using the fact in the browser the scope * is history if scope->outerObject->innerObject != scope. */ static JSBool ShouldDeferCloseHook(JSContext *cx, JSGenerator *gen, JSBool *defer) { JSObject *parent, *obj; JSClass *clasp; JSExtendedClass *xclasp; /* * This is called outside any locks, so use thread-safe macros to access * parent and classes. */ *defer = JS_FALSE; parent = OBJ_GET_PARENT(cx, gen->obj); clasp = OBJ_GET_CLASS(cx, parent); if (clasp->flags & JSCLASS_IS_EXTENDED) { xclasp = (JSExtendedClass *)clasp; if (xclasp->outerObject) { obj = xclasp->outerObject(cx, parent); if (!obj) return JS_FALSE; OBJ_TO_INNER_OBJECT(cx, obj); if (!obj) return JS_FALSE; *defer = obj != parent; } } #ifdef DEBUG_igor if (*defer) { fprintf(stderr, "GEN: deferring, gen=%p parent=%p\n", (void *)gen, (void *)parent); } #endif return JS_TRUE; } /* * Find all unreachable generators and move them to the todo queue from * rt->gcCloseState.reachableList to execute thier close hooks after the GC * cycle completes. To ensure liveness during the sweep phase we mark all * generators we are going to close later. */ static void FindAndMarkObjectsToClose(JSContext *cx, JSGCInvocationKind gckind, JSGenerator **todoQueueTail) { JSRuntime *rt; JSGenerator *todo, **genp, *gen; rt = cx->runtime; todo = NULL; genp = &rt->gcCloseState.reachableList; while ((gen = *genp) != NULL) { if (*js_GetGCThingFlags(gen->obj) & GCF_MARK) { genp = &gen->next; } else { /* Generator must not be executing when it becomes unreachable. */ JS_ASSERT(gen->state == JSGEN_NEWBORN || gen->state == JSGEN_OPEN || gen->state == JSGEN_CLOSED); *genp = gen->next; if (gen->state == JSGEN_OPEN && js_FindFinallyHandler(gen->frame.script, gen->frame.pc) && CanScheduleCloseHook(gen)) { /* * Generator yielded inside a try with a finally block. * Schedule it for closing. * * We keep generators that yielded outside try-with-finally * with gen->state == JSGEN_OPEN. The finalizer must deal with * open generators as we may skip the close hooks, see below. */ gen->next = NULL; *todoQueueTail = gen; todoQueueTail = &gen->next; if (!todo) todo = gen; METER(JS_ASSERT(rt->gcStats.nclose)); METER(rt->gcStats.nclose--); METER(rt->gcStats.closelater++); METER(rt->gcStats.maxcloselater = JS_MAX(rt->gcStats.maxcloselater, rt->gcStats.closelater)); } } } if (gckind == GC_LAST_CONTEXT) { /* * Remove scheduled hooks on shutdown as it is too late to run them: * we do not allow execution of arbitrary scripts at this point. */ rt->gcCloseState.todoQueue = NULL; } else { /* * Mark just-found unreachable generators *after* we scan the global * list to prevent a generator that refers to other unreachable * generators from keeping them on gcCloseState.reachableList. */ for (gen = todo; gen; gen = gen->next) GC_MARK(cx, gen->obj, "newly scheduled generator"); } } /* * Mark unreachable generators already scheduled to close and return the tail * pointer to JSGCCloseState.todoQueue. */ static JSGenerator ** MarkScheduledGenerators(JSContext *cx) { JSRuntime *rt; JSGenerator **genp, *gen; rt = cx->runtime; genp = &rt->gcCloseState.todoQueue; while ((gen = *genp) != NULL) { if (CanScheduleCloseHook(gen)) { GC_MARK(cx, gen->obj, "scheduled generator"); genp = &gen->next; } else { /* Discard the generator from the list if its schedule is over. */ *genp = gen->next; METER(JS_ASSERT(rt->gcStats.closelater > 0)); METER(rt->gcStats.closelater--); } } return genp; } #ifdef JS_THREADSAFE # define GC_RUNNING_CLOSE_HOOKS_PTR(cx) \ (&(cx)->thread->gcRunningCloseHooks) #else # define GC_RUNNING_CLOSE_HOOKS_PTR(cx) \ (&(cx)->runtime->gcCloseState.runningCloseHook) #endif typedef struct JSTempCloseList { JSTempValueRooter tvr; JSGenerator *head; } JSTempCloseList; JS_STATIC_DLL_CALLBACK(void) mark_temp_close_list(JSContext *cx, JSTempValueRooter *tvr) { JSTempCloseList *list = (JSTempCloseList *)tvr; JSGenerator *gen; for (gen = list->head; gen; gen = gen->next) GC_MARK(cx, gen->obj, "temp list generator"); } #define JS_PUSH_TEMP_CLOSE_LIST(cx, tempList) \ JS_PUSH_TEMP_ROOT_MARKER(cx, mark_temp_close_list, &(tempList)->tvr) #define JS_POP_TEMP_CLOSE_LIST(cx, tempList) \ JS_BEGIN_MACRO \ JS_ASSERT((tempList)->tvr.u.marker == mark_temp_close_list); \ JS_POP_TEMP_ROOT(cx, &(tempList)->tvr); \ JS_END_MACRO JSBool js_RunCloseHooks(JSContext *cx) { JSRuntime *rt; JSTempCloseList tempList; JSStackFrame *fp; JSGenerator **genp, *gen; JSBool ok, defer; #if JS_GCMETER uint32 deferCount; #endif rt = cx->runtime; /* * It is OK to access todoQueue outside the lock here. When many threads * update the todo list, accessing some older value of todoQueue in the * worst case just delays the excution of close hooks. */ if (!rt->gcCloseState.todoQueue) return JS_TRUE; /* * To prevent an infinite loop when a close hook creats more objects with * close hooks and then triggers GC we ignore recursive invocations of * js_RunCloseHooks and limit number of hooks to execute to the initial * size of the list. */ if (*GC_RUNNING_CLOSE_HOOKS_PTR(cx)) return JS_TRUE; *GC_RUNNING_CLOSE_HOOKS_PTR(cx) = JS_TRUE; JS_LOCK_GC(rt); tempList.head = rt->gcCloseState.todoQueue; JS_PUSH_TEMP_CLOSE_LIST(cx, &tempList); rt->gcCloseState.todoQueue = NULL; METER(rt->gcStats.closelater = 0); rt->gcPoke = JS_TRUE; JS_UNLOCK_GC(rt); /* * Set aside cx->fp since we do not want a close hook using caller or * other means to backtrace into whatever stack might be active when * running the hook. We store the current frame on the dormant list to * protect against GC that the hook can trigger. */ fp = cx->fp; if (fp) { JS_ASSERT(!fp->dormantNext); fp->dormantNext = cx->dormantFrameChain; cx->dormantFrameChain = fp; } cx->fp = NULL; genp = &tempList.head; ok = JS_TRUE; while ((gen = *genp) != NULL) { ok = ShouldDeferCloseHook(cx, gen, &defer); if (!ok) { /* Quit ASAP discarding the hook. */ *genp = gen->next; break; } if (defer) { genp = &gen->next; METER(deferCount++); continue; } ok = js_CloseGeneratorObject(cx, gen); /* * Unlink the generator after closing it to make sure it always stays * rooted through tempList. */ *genp = gen->next; if (cx->throwing) { /* * Report the exception thrown by the close hook and continue to * execute the rest of the hooks. */ if (!js_ReportUncaughtException(cx)) JS_ClearPendingException(cx); ok = JS_TRUE; } else if (!ok) { /* * Assume this is a stop signal from the branch callback or * other quit ASAP condition. Break execution until the next * invocation of js_RunCloseHooks. */ break; } } cx->fp = fp; if (fp) { JS_ASSERT(cx->dormantFrameChain == fp); cx->dormantFrameChain = fp->dormantNext; fp->dormantNext = NULL; } if (tempList.head) { /* * Some close hooks were not yet executed, put them back into the * scheduled list. */ while ((gen = *genp) != NULL) { genp = &gen->next; METER(deferCount++); } /* Now genp is a pointer to the tail of tempList. */ JS_LOCK_GC(rt); *genp = rt->gcCloseState.todoQueue; rt->gcCloseState.todoQueue = tempList.head; METER(rt->gcStats.closelater += deferCount); METER(rt->gcStats.maxcloselater = JS_MAX(rt->gcStats.maxcloselater, rt->gcStats.closelater)); JS_UNLOCK_GC(rt); } JS_POP_TEMP_CLOSE_LIST(cx, &tempList); *GC_RUNNING_CLOSE_HOOKS_PTR(cx) = JS_FALSE; return ok; } #endif /* JS_HAS_GENERATORS */ #if defined(DEBUG_brendan) || defined(DEBUG_timeless) #define DEBUG_gchist #endif #ifdef DEBUG_gchist #define NGCHIST 64 static struct GCHist { JSBool lastDitch; JSGCThing *freeList; } gchist[NGCHIST]; unsigned gchpos; #endif void * js_NewGCThing(JSContext *cx, uintN flags, size_t nbytes) { JSRuntime *rt; uintN flindex; JSBool doGC; JSGCThing *thing; uint8 *flagp, *firstPage; JSGCArenaList *arenaList; jsuword offset; JSGCArena *a; JSLocalRootStack *lrs; #ifdef JS_THREADSAFE JSBool gcLocked; uintN localMallocBytes; JSGCThing **flbase, **lastptr; JSGCThing *tmpthing; uint8 *tmpflagp; uintN maxFreeThings; /* max to take from the global free list */ METER(size_t nfree); #endif rt = cx->runtime; METER(rt->gcStats.alloc++); /* this is not thread-safe */ nbytes = JS_ROUNDUP(nbytes, sizeof(JSGCThing)); flindex = GC_FREELIST_INDEX(nbytes); #ifdef JS_THREADSAFE gcLocked = JS_FALSE; JS_ASSERT(cx->thread); flbase = cx->thread->gcFreeLists; JS_ASSERT(flbase); thing = flbase[flindex]; localMallocBytes = cx->thread->gcMallocBytes; if (thing && rt->gcMaxMallocBytes - rt->gcMallocBytes > localMallocBytes) { flagp = thing->flagp; flbase[flindex] = thing->next; METER(rt->gcStats.localalloc++); /* this is not thread-safe */ goto success; } JS_LOCK_GC(rt); gcLocked = JS_TRUE; /* Transfer thread-local counter to global one. */ if (localMallocBytes != 0) { cx->thread->gcMallocBytes = 0; if (rt->gcMaxMallocBytes - rt->gcMallocBytes < localMallocBytes) rt->gcMallocBytes = rt->gcMaxMallocBytes; else rt->gcMallocBytes += localMallocBytes; } #endif JS_ASSERT(!rt->gcRunning); if (rt->gcRunning) { METER(rt->gcStats.finalfail++); JS_UNLOCK_GC(rt); return NULL; } #ifdef TOO_MUCH_GC #ifdef WAY_TOO_MUCH_GC rt->gcPoke = JS_TRUE; #endif doGC = JS_TRUE; #else doGC = (rt->gcMallocBytes >= rt->gcMaxMallocBytes); #endif arenaList = &rt->gcArenaList[flindex]; for (;;) { if (doGC) { /* * Keep rt->gcLock across the call into js_GC so we don't starve * and lose to racing threads who deplete the heap just after * js_GC has replenished it (or has synchronized with a racing * GC that collected a bunch of garbage). This unfair scheduling * can happen on certain operating systems. For the gory details, * see bug 162779 at https://bugzilla.mozilla.org/. */ js_GC(cx, GC_LAST_DITCH); METER(rt->gcStats.retry++); } /* Try to get thing from the free list. */ thing = arenaList->freeList; if (thing) { arenaList->freeList = thing->next; flagp = thing->flagp; JS_ASSERT(*flagp & GCF_FINAL); METER(arenaList->stats.freelen--); METER(arenaList->stats.recycle++); #ifdef JS_THREADSAFE /* * Refill the local free list by taking several things from the * global free list unless we are still at rt->gcMaxMallocBytes * barrier or the free list is already populated. The former * happens when GC is canceled due to !gcCallback(cx, JSGC_BEGIN) * or no gcPoke. The latter is caused via allocating new things * in gcCallback(cx, JSGC_END). */ if (rt->gcMallocBytes >= rt->gcMaxMallocBytes || flbase[flindex]) break; tmpthing = arenaList->freeList; if (tmpthing) { maxFreeThings = MAX_THREAD_LOCAL_THINGS; do { if (!tmpthing->next) break; tmpthing = tmpthing->next; } while (--maxFreeThings != 0); flbase[flindex] = arenaList->freeList; arenaList->freeList = tmpthing->next; tmpthing->next = NULL; } #endif break; } /* Allocate from the tail of last arena or from new arena if we can. */ if ((arenaList->last && arenaList->lastLimit != GC_THINGS_SIZE) || NewGCArena(rt, arenaList)) { offset = arenaList->lastLimit; if ((offset & GC_PAGE_MASK) == 0) { /* * Skip JSGCPageInfo record located at GC_PAGE_SIZE boundary. */ offset += PAGE_THING_GAP(nbytes); } JS_ASSERT(offset + nbytes <= GC_THINGS_SIZE); arenaList->lastLimit = (uint16)(offset + nbytes); a = arenaList->last; firstPage = (uint8 *)FIRST_THING_PAGE(a); thing = (JSGCThing *)(firstPage + offset); flagp = a->base + offset / sizeof(JSGCThing); if (flagp >= firstPage) flagp += GC_THINGS_SIZE; METER(++arenaList->stats.nthings); METER(arenaList->stats.maxthings = JS_MAX(arenaList->stats.nthings, arenaList->stats.maxthings)); #ifdef JS_THREADSAFE /* * Refill the local free list by taking free things from the last * arena. Prefer to order free things by ascending address in the * (unscientific) hope of better cache locality. */ if (rt->gcMallocBytes >= rt->gcMaxMallocBytes || flbase[flindex]) break; METER(nfree = 0); lastptr = &flbase[flindex]; maxFreeThings = MAX_THREAD_LOCAL_THINGS; for (offset = arenaList->lastLimit; offset != GC_THINGS_SIZE && maxFreeThings-- != 0; offset += nbytes) { if ((offset & GC_PAGE_MASK) == 0) offset += PAGE_THING_GAP(nbytes); JS_ASSERT(offset + nbytes <= GC_THINGS_SIZE); tmpflagp = a->base + offset / sizeof(JSGCThing); if (tmpflagp >= firstPage) tmpflagp += GC_THINGS_SIZE; tmpthing = (JSGCThing *)(firstPage + offset); tmpthing->flagp = tmpflagp; *tmpflagp = GCF_FINAL; /* signifying that thing is free */ *lastptr = tmpthing; lastptr = &tmpthing->next; METER(++nfree); } arenaList->lastLimit = offset; *lastptr = NULL; METER(arenaList->stats.freelen += nfree); #endif break; } /* Consider doing a "last ditch" GC unless already tried. */ if (doGC) goto fail; rt->gcPoke = JS_TRUE; doGC = JS_TRUE; } /* We successfully allocated the thing. */ #ifdef JS_THREADSAFE success: #endif lrs = cx->localRootStack; if (lrs) { /* * If we're in a local root scope, don't set newborn[type] at all, to * avoid entraining garbage from it for an unbounded amount of time * on this context. A caller will leave the local root scope and pop * this reference, allowing thing to be GC'd if it has no other refs. * See JS_EnterLocalRootScope and related APIs. */ if (js_PushLocalRoot(cx, lrs, (jsval) thing) < 0) { /* * When we fail for a thing allocated through the tail of the last * arena, thing's flag byte is not initialized. So to prevent GC * accessing the uninitialized flags during the finalization, we * always mark the thing as final. See bug 337407. */ *flagp = GCF_FINAL; goto fail; } } else { /* * No local root scope, so we're stuck with the old, fragile model of * depending on a pigeon-hole newborn per type per context. */ cx->weakRoots.newborn[flags & GCF_TYPEMASK] = thing; } /* We can't fail now, so update flags and rt->gc{,Private}Bytes. */ *flagp = (uint8)flags; /* * Clear thing before unlocking in case a GC run is about to scan it, * finding it via newborn[]. */ thing->next = NULL; thing->flagp = NULL; #ifdef DEBUG_gchist gchist[gchpos].lastDitch = doGC; gchist[gchpos].freeList = rt->gcArenaList[flindex].freeList; if (++gchpos == NGCHIST) gchpos = 0; #endif METER(if (flags & GCF_LOCK) rt->gcStats.lockborn++); METER(++rt->gcArenaList[flindex].stats.totalnew); #ifdef JS_THREADSAFE if (gcLocked) JS_UNLOCK_GC(rt); #endif return thing; fail: #ifdef JS_THREADSAFE if (gcLocked) JS_UNLOCK_GC(rt); #endif METER(rt->gcStats.fail++); JS_ReportOutOfMemory(cx); return NULL; } JSBool js_LockGCThing(JSContext *cx, void *thing) { JSBool ok = js_LockGCThingRT(cx->runtime, thing); if (!ok) JS_ReportOutOfMemory(cx); return ok; } /* * Deep GC-things can't be locked just by setting the GCF_LOCK bit, because * their descendants must be marked by the GC. To find them during the mark * phase, they are added to rt->gcLocksHash, which is created lazily. * * NB: we depend on the order of GC-thing type indexes here! */ #define GC_TYPE_IS_STRING(t) ((t) == GCX_STRING || \ (t) >= GCX_EXTERNAL_STRING) #define GC_TYPE_IS_XML(t) ((unsigned)((t) - GCX_NAMESPACE) <= \ (unsigned)(GCX_XML - GCX_NAMESPACE)) #define GC_TYPE_IS_DEEP(t) ((t) == GCX_OBJECT || GC_TYPE_IS_XML(t)) #define IS_DEEP_STRING(t,o) (GC_TYPE_IS_STRING(t) && \ JSSTRING_IS_DEPENDENT((JSString *)(o))) #define GC_THING_IS_DEEP(t,o) (GC_TYPE_IS_DEEP(t) || IS_DEEP_STRING(t, o)) /* This is compatible with JSDHashEntryStub. */ typedef struct JSGCLockHashEntry { JSDHashEntryHdr hdr; const JSGCThing *thing; uint32 count; } JSGCLockHashEntry; JSBool js_LockGCThingRT(JSRuntime *rt, void *thing) { JSBool ok, deep; uint8 *flagp; uintN flags, lock, type; JSGCLockHashEntry *lhe; ok = JS_TRUE; if (!thing) return ok; flagp = js_GetGCThingFlags(thing); JS_LOCK_GC(rt); flags = *flagp; lock = (flags & GCF_LOCK); type = (flags & GCF_TYPEMASK); deep = GC_THING_IS_DEEP(type, thing); /* * Avoid adding a rt->gcLocksHash entry for shallow things until someone * nests a lock -- then start such an entry with a count of 2, not 1. */ if (lock || deep) { if (!rt->gcLocksHash) { rt->gcLocksHash = JS_NewDHashTable(JS_DHashGetStubOps(), NULL, sizeof(JSGCLockHashEntry), GC_ROOTS_SIZE); if (!rt->gcLocksHash) { ok = JS_FALSE; goto done; } } else if (lock == 0) { #ifdef DEBUG JSDHashEntryHdr *hdr = JS_DHashTableOperate(rt->gcLocksHash, thing, JS_DHASH_LOOKUP); JS_ASSERT(JS_DHASH_ENTRY_IS_FREE(hdr)); #endif } lhe = (JSGCLockHashEntry *) JS_DHashTableOperate(rt->gcLocksHash, thing, JS_DHASH_ADD); if (!lhe) { ok = JS_FALSE; goto done; } if (!lhe->thing) { lhe->thing = thing; lhe->count = deep ? 1 : 2; } else { JS_ASSERT(lhe->count >= 1); lhe->count++; } } *flagp = (uint8)(flags | GCF_LOCK); METER(rt->gcStats.lock++); ok = JS_TRUE; done: JS_UNLOCK_GC(rt); return ok; } JSBool js_UnlockGCThingRT(JSRuntime *rt, void *thing) { uint8 *flagp, flags; JSGCLockHashEntry *lhe; if (!thing) return JS_TRUE; flagp = js_GetGCThingFlags(thing); JS_LOCK_GC(rt); flags = *flagp; if (flags & GCF_LOCK) { if (!rt->gcLocksHash || (lhe = (JSGCLockHashEntry *) JS_DHashTableOperate(rt->gcLocksHash, thing, JS_DHASH_LOOKUP), JS_DHASH_ENTRY_IS_FREE(&lhe->hdr))) { /* Shallow GC-thing with an implicit lock count of 1. */ JS_ASSERT(!GC_THING_IS_DEEP(flags & GCF_TYPEMASK, thing)); } else { /* Basis or nested unlock of a deep thing, or nested of shallow. */ if (--lhe->count != 0) goto out; JS_DHashTableOperate(rt->gcLocksHash, thing, JS_DHASH_REMOVE); } *flagp = (uint8)(flags & ~GCF_LOCK); } rt->gcPoke = JS_TRUE; out: METER(rt->gcStats.unlock++); JS_UNLOCK_GC(rt); return JS_TRUE; } #ifdef GC_MARK_DEBUG #include #include "jsprf.h" typedef struct GCMarkNode GCMarkNode; struct GCMarkNode { void *thing; const char *name; GCMarkNode *next; GCMarkNode *prev; }; JS_FRIEND_DATA(FILE *) js_DumpGCHeap; JS_EXPORT_DATA(void *) js_LiveThingToFind; #ifdef HAVE_XPCONNECT #include "dump_xpc.h" #endif static void GetObjSlotName(JSScope *scope, JSObject *obj, uint32 slot, char *buf, size_t bufsize) { jsval nval; JSScopeProperty *sprop; JSClass *clasp; uint32 key; const char *slotname; if (!scope) { JS_snprintf(buf, bufsize, "**UNKNOWN OBJECT MAP ENTRY**"); return; } sprop = SCOPE_LAST_PROP(scope); while (sprop && sprop->slot != slot) sprop = sprop->parent; if (!sprop) { switch (slot) { case JSSLOT_PROTO: JS_snprintf(buf, bufsize, "__proto__"); break; case JSSLOT_PARENT: JS_snprintf(buf, bufsize, "__parent__"); break; default: slotname = NULL; clasp = LOCKED_OBJ_GET_CLASS(obj); if (clasp->flags & JSCLASS_IS_GLOBAL) { key = slot - JSSLOT_START(clasp); #define JS_PROTO(name,code,init) \ if ((code) == key) { slotname = js_##name##_str; goto found; } #include "jsproto.tbl" #undef JS_PROTO } found: if (slotname) JS_snprintf(buf, bufsize, "CLASS_OBJECT(%s)", slotname); else JS_snprintf(buf, bufsize, "**UNKNOWN SLOT %ld**", (long)slot); break; } } else { nval = ID_TO_VALUE(sprop->id); if (JSVAL_IS_INT(nval)) { JS_snprintf(buf, bufsize, "%ld", (long)JSVAL_TO_INT(nval)); } else if (JSVAL_IS_STRING(nval)) { JS_snprintf(buf, bufsize, "%s", JS_GetStringBytes(JSVAL_TO_STRING(nval))); } else { JS_snprintf(buf, bufsize, "**FINALIZED ATOM KEY**"); } } } static const char * gc_object_class_name(void* thing) { uint8 *flagp = js_GetGCThingFlags(thing); const char *className = ""; static char depbuf[32]; switch (*flagp & GCF_TYPEMASK) { case GCX_OBJECT: { JSObject *obj = (JSObject *)thing; JSClass *clasp = JSVAL_TO_PRIVATE(obj->slots[JSSLOT_CLASS]); className = clasp->name; #ifdef HAVE_XPCONNECT if (clasp->flags & JSCLASS_PRIVATE_IS_NSISUPPORTS) { jsval privateValue = obj->slots[JSSLOT_PRIVATE]; JS_ASSERT(clasp->flags & JSCLASS_HAS_PRIVATE); if (!JSVAL_IS_VOID(privateValue)) { void *privateThing = JSVAL_TO_PRIVATE(privateValue); const char *xpcClassName = GetXPCObjectClassName(privateThing); if (xpcClassName) className = xpcClassName; } } #endif break; } case GCX_STRING: case GCX_MUTABLE_STRING: { JSString *str = (JSString *)thing; if (JSSTRING_IS_DEPENDENT(str)) { JS_snprintf(depbuf, sizeof depbuf, "start:%u, length:%u", JSSTRDEP_START(str), JSSTRDEP_LENGTH(str)); className = depbuf; } else { className = "string"; } break; } case GCX_DOUBLE: className = "double"; break; } return className; } static void gc_dump_thing(JSContext *cx, JSGCThing *thing, FILE *fp) { GCMarkNode *prev = (GCMarkNode *)cx->gcCurrentMarkNode; GCMarkNode *next = NULL; char *path = NULL; while (prev) { next = prev; prev = prev->prev; } while (next) { uint8 nextFlags = *js_GetGCThingFlags(next->thing); if ((nextFlags & GCF_TYPEMASK) == GCX_OBJECT) { path = JS_sprintf_append(path, "%s(%s @ 0x%08p).", next->name, gc_object_class_name(next->thing), (JSObject*)next->thing); } else { path = JS_sprintf_append(path, "%s(%s).", next->name, gc_object_class_name(next->thing)); } next = next->next; } if (!path) return; fprintf(fp, "%08lx ", (long)thing); switch (*js_GetGCThingFlags(thing) & GCF_TYPEMASK) { case GCX_OBJECT: { JSObject *obj = (JSObject *)thing; jsval privateValue = obj->slots[JSSLOT_PRIVATE]; void *privateThing = JSVAL_IS_VOID(privateValue) ? NULL : JSVAL_TO_PRIVATE(privateValue); const char *className = gc_object_class_name(thing); fprintf(fp, "object %8p %s", privateThing, className); break; } #if JS_HAS_XML_SUPPORT case GCX_NAMESPACE: { JSXMLNamespace *ns = (JSXMLNamespace *)thing; fprintf(fp, "namespace %s:%s", JS_GetStringBytes(ns->prefix), JS_GetStringBytes(ns->uri)); break; } case GCX_QNAME: { JSXMLQName *qn = (JSXMLQName *)thing; fprintf(fp, "qname %s(%s):%s", JS_GetStringBytes(qn->prefix), JS_GetStringBytes(qn->uri), JS_GetStringBytes(qn->localName)); break; } case GCX_XML: { extern const char *js_xml_class_str[]; JSXML *xml = (JSXML *)thing; fprintf(fp, "xml %8p %s", xml, js_xml_class_str[xml->xml_class]); break; } #endif case GCX_DOUBLE: fprintf(fp, "double %g", *(jsdouble *)thing); break; case GCX_PRIVATE: fprintf(fp, "private %8p", (void *)thing); break; default: fprintf(fp, "string %s", JS_GetStringBytes((JSString *)thing)); break; } fprintf(fp, " via %s\n", path); free(path); } void js_MarkNamedGCThing(JSContext *cx, void *thing, const char *name) { GCMarkNode markNode; if (!thing) return; markNode.thing = thing; markNode.name = name; markNode.next = NULL; markNode.prev = (GCMarkNode *)cx->gcCurrentMarkNode; if (markNode.prev) markNode.prev->next = &markNode; cx->gcCurrentMarkNode = &markNode; if (thing == js_LiveThingToFind) { /* * Dump js_LiveThingToFind each time we reach it during the marking * phase of GC to print all live references to the thing. */ gc_dump_thing(cx, thing, stderr); } js_MarkGCThing(cx, thing); if (markNode.prev) markNode.prev->next = NULL; cx->gcCurrentMarkNode = markNode.prev; } #endif /* !GC_MARK_DEBUG */ static void gc_mark_atom_key_thing(void *thing, void *arg) { JSContext *cx = (JSContext *) arg; GC_MARK(cx, thing, "atom"); } void js_MarkAtom(JSContext *cx, JSAtom *atom) { jsval key; if (atom->flags & ATOM_MARK) return; atom->flags |= ATOM_MARK; key = ATOM_KEY(atom); if (JSVAL_IS_GCTHING(key)) { #ifdef GC_MARK_DEBUG char name[32]; if (JSVAL_IS_STRING(key)) { JS_snprintf(name, sizeof name, "'%s'", JS_GetStringBytes(JSVAL_TO_STRING(key))); } else { JS_snprintf(name, sizeof name, "<%x>", key); } #endif GC_MARK(cx, JSVAL_TO_GCTHING(key), name); } if (atom->flags & ATOM_HIDDEN) js_MarkAtom(cx, atom->entry.value); } static void AddThingToUnscannedBag(JSRuntime *rt, void *thing, uint8 *flagp); static void MarkGCThingChildren(JSContext *cx, void *thing, uint8 *flagp, JSBool shouldCheckRecursion) { JSRuntime *rt; JSObject *obj; jsval v, *vp, *end; void *next_thing; uint8 *next_flagp; JSString *str; #ifdef JS_GCMETER uint32 tailCallNesting; #endif #ifdef GC_MARK_DEBUG JSScope *scope; char name[32]; #endif /* * With JS_GC_ASSUME_LOW_C_STACK defined the mark phase of GC always * uses the non-recursive code that otherwise would be called only on * a low C stack condition. */ #ifdef JS_GC_ASSUME_LOW_C_STACK # define RECURSION_TOO_DEEP() shouldCheckRecursion #else int stackDummy; # define RECURSION_TOO_DEEP() (shouldCheckRecursion && \ !JS_CHECK_STACK_SIZE(cx, stackDummy)) #endif rt = cx->runtime; METER(tailCallNesting = 0); METER(if (++rt->gcStats.cdepth > rt->gcStats.maxcdepth) rt->gcStats.maxcdepth = rt->gcStats.cdepth); #ifndef GC_MARK_DEBUG start: #endif JS_ASSERT(flagp); JS_ASSERT(*flagp & GCF_MARK); /* the caller must already mark the thing */ METER(if (++rt->gcStats.depth > rt->gcStats.maxdepth) rt->gcStats.maxdepth = rt->gcStats.depth); #ifdef GC_MARK_DEBUG if (js_DumpGCHeap) gc_dump_thing(cx, thing, js_DumpGCHeap); #endif switch (*flagp & GCF_TYPEMASK) { case GCX_OBJECT: if (RECURSION_TOO_DEEP()) goto add_to_unscanned_bag; /* If obj->slots is null, obj must be a newborn. */ obj = (JSObject *) thing; vp = obj->slots; if (!vp) break; /* Mark slots if they are small enough to be GC-allocated. */ if ((vp[-1] + 1) * sizeof(jsval) <= GC_NBYTES_MAX) GC_MARK(cx, vp - 1, "slots"); /* Set up local variables to loop over unmarked things. */ end = vp + ((obj->map->ops->mark) ? obj->map->ops->mark(cx, obj, NULL) : JS_MIN(obj->map->freeslot, obj->map->nslots)); thing = NULL; flagp = NULL; #ifdef GC_MARK_DEBUG scope = OBJ_IS_NATIVE(obj) ? OBJ_SCOPE(obj) : NULL; #endif for (; vp != end; ++vp) { v = *vp; if (!JSVAL_IS_GCTHING(v) || v == JSVAL_NULL) continue; next_thing = JSVAL_TO_GCTHING(v); if (next_thing == thing) continue; next_flagp = js_GetGCThingFlags(next_thing); if (*next_flagp & GCF_MARK) continue; JS_ASSERT(*next_flagp != GCF_FINAL); if (thing) { #ifdef GC_MARK_DEBUG GC_MARK(cx, thing, name); #else *flagp |= GCF_MARK; MarkGCThingChildren(cx, thing, flagp, JS_TRUE); #endif if (*next_flagp & GCF_MARK) { /* * This happens when recursive MarkGCThingChildren marks * the thing with flags referred by *next_flagp. */ thing = NULL; continue; } } #ifdef GC_MARK_DEBUG GetObjSlotName(scope, obj, vp - obj->slots, name, sizeof name); #endif thing = next_thing; flagp = next_flagp; } if (thing) { /* * thing came from the last unmarked GC-thing slot and we * can optimize tail recursion. * * Since we already know that there is enough C stack space, * we clear shouldCheckRecursion to avoid extra checking in * RECURSION_TOO_DEEP. */ shouldCheckRecursion = JS_FALSE; goto on_tail_recursion; } break; #ifdef DEBUG case GCX_STRING: str = (JSString *)thing; JS_ASSERT(!JSSTRING_IS_DEPENDENT(str)); break; #endif case GCX_MUTABLE_STRING: str = (JSString *)thing; if (!JSSTRING_IS_DEPENDENT(str)) break; thing = JSSTRDEP_BASE(str); flagp = js_GetGCThingFlags(thing); if (*flagp & GCF_MARK) break; #ifdef GC_MARK_DEBUG strcpy(name, "base"); #endif /* Fallthrough to code to deal with the tail recursion. */ on_tail_recursion: #ifdef GC_MARK_DEBUG /* * Do not eliminate C recursion when debugging to allow * js_MarkNamedGCThing to build a full dump of live GC * things. */ GC_MARK(cx, thing, name); break; #else /* Eliminate tail recursion for the last unmarked child. */ JS_ASSERT(*flagp != GCF_FINAL); METER(++tailCallNesting); *flagp |= GCF_MARK; goto start; #endif #if JS_HAS_XML_SUPPORT case GCX_NAMESPACE: if (RECURSION_TOO_DEEP()) goto add_to_unscanned_bag; js_MarkXMLNamespace(cx, (JSXMLNamespace *)thing); break; case GCX_QNAME: if (RECURSION_TOO_DEEP()) goto add_to_unscanned_bag; js_MarkXMLQName(cx, (JSXMLQName *)thing); break; case GCX_XML: if (RECURSION_TOO_DEEP()) goto add_to_unscanned_bag; js_MarkXML(cx, (JSXML *)thing); break; #endif add_to_unscanned_bag: AddThingToUnscannedBag(cx->runtime, thing, flagp); break; } #undef RECURSION_TOO_DEEP METER(rt->gcStats.depth -= 1 + tailCallNesting); METER(rt->gcStats.cdepth--); } /* * Avoid using PAGE_THING_GAP inside this macro to optimize the * thingsPerUnscannedChunk calculation when thingSize is a power of two. */ #define GET_GAP_AND_CHUNK_SPAN(thingSize, thingsPerUnscannedChunk, pageGap) \ JS_BEGIN_MACRO \ if (0 == ((thingSize) & ((thingSize) - 1))) { \ pageGap = (thingSize); \ thingsPerUnscannedChunk = ((GC_PAGE_SIZE / (thingSize)) \ + JS_BITS_PER_WORD - 1) \ >> JS_BITS_PER_WORD_LOG2; \ } else { \ pageGap = GC_PAGE_SIZE % (thingSize); \ thingsPerUnscannedChunk = JS_HOWMANY(GC_PAGE_SIZE / (thingSize), \ JS_BITS_PER_WORD); \ } \ JS_END_MACRO static void AddThingToUnscannedBag(JSRuntime *rt, void *thing, uint8 *flagp) { JSGCPageInfo *pi; JSGCArena *arena; size_t thingSize; size_t thingsPerUnscannedChunk; size_t pageGap; size_t chunkIndex; jsuword bit; /* Things from delayed scanning bag are marked as GCF_MARK | GCF_FINAL. */ JS_ASSERT((*flagp & (GCF_MARK | GCF_FINAL)) == GCF_MARK); *flagp |= GCF_FINAL; METER(rt->gcStats.unscanned++); #ifdef DEBUG ++rt->gcUnscannedBagSize; METER(if (rt->gcUnscannedBagSize > rt->gcStats.maxunscanned) rt->gcStats.maxunscanned = rt->gcUnscannedBagSize); #endif pi = THING_TO_PAGE(thing); arena = PAGE_TO_ARENA(pi); thingSize = arena->list->thingSize; GET_GAP_AND_CHUNK_SPAN(thingSize, thingsPerUnscannedChunk, pageGap); chunkIndex = (((jsuword)thing & GC_PAGE_MASK) - pageGap) / (thingSize * thingsPerUnscannedChunk); JS_ASSERT(chunkIndex < JS_BITS_PER_WORD); bit = (jsuword)1 << chunkIndex; if (pi->unscannedBitmap != 0) { JS_ASSERT(rt->gcUnscannedArenaStackTop); if (thingsPerUnscannedChunk != 1) { if (pi->unscannedBitmap & bit) { /* Chunk already contains things to scan later. */ return; } } else { /* * The chunk must not contain things to scan later if there is * only one thing per chunk. */ JS_ASSERT(!(pi->unscannedBitmap & bit)); } pi->unscannedBitmap |= bit; JS_ASSERT(arena->unscannedPages & ((size_t)1 << PAGE_INDEX(pi))); } else { /* * The thing is the first unscanned thing in the page, set the bit * corresponding to this page arena->unscannedPages. */ pi->unscannedBitmap = bit; JS_ASSERT(PAGE_INDEX(pi) < JS_BITS_PER_WORD); bit = (jsuword)1 << PAGE_INDEX(pi); JS_ASSERT(!(arena->unscannedPages & bit)); if (arena->unscannedPages != 0) { arena->unscannedPages |= bit; JS_ASSERT(arena->prevUnscanned); JS_ASSERT(rt->gcUnscannedArenaStackTop); } else { /* * The thing is the first unscanned thing in the whole arena, push * the arena on the stack of unscanned arenas unless the arena * has already been pushed. We detect that through prevUnscanned * field which is NULL only for not yet pushed arenas. To ensure * that prevUnscanned != NULL even when the stack contains one * element, we make prevUnscanned for the arena at the bottom * to point to itself. * * See comments in ScanDelayedChildren. */ arena->unscannedPages = bit; if (!arena->prevUnscanned) { if (!rt->gcUnscannedArenaStackTop) { /* Stack was empty, mark the arena as bottom element. */ arena->prevUnscanned = arena; } else { JS_ASSERT(rt->gcUnscannedArenaStackTop->prevUnscanned); arena->prevUnscanned = rt->gcUnscannedArenaStackTop; } rt->gcUnscannedArenaStackTop = arena; } } } JS_ASSERT(rt->gcUnscannedArenaStackTop); } static void ScanDelayedChildren(JSContext *cx) { JSRuntime *rt; JSGCArena *arena; size_t thingSize; size_t thingsPerUnscannedChunk; size_t pageGap; size_t pageIndex; JSGCPageInfo *pi; size_t chunkIndex; size_t thingOffset, thingLimit; JSGCThing *thing; uint8 *flagp; JSGCArena *prevArena; rt = cx->runtime; arena = rt->gcUnscannedArenaStackTop; if (!arena) { JS_ASSERT(rt->gcUnscannedBagSize == 0); return; } init_size: thingSize = arena->list->thingSize; GET_GAP_AND_CHUNK_SPAN(thingSize, thingsPerUnscannedChunk, pageGap); for (;;) { /* * The following assert verifies that the current arena belongs to * the unscan stack since AddThingToUnscannedBag ensures that even * for stack's bottom prevUnscanned != NULL but rather points to self. */ JS_ASSERT(arena->prevUnscanned); JS_ASSERT(rt->gcUnscannedArenaStackTop->prevUnscanned); while (arena->unscannedPages != 0) { pageIndex = JS_FLOOR_LOG2W(arena->unscannedPages); JS_ASSERT(pageIndex < GC_PAGE_COUNT); pi = (JSGCPageInfo *)(FIRST_THING_PAGE(arena) + pageIndex * GC_PAGE_SIZE); JS_ASSERT(pi->unscannedBitmap); chunkIndex = JS_FLOOR_LOG2W(pi->unscannedBitmap); pi->unscannedBitmap &= ~((jsuword)1 << chunkIndex); if (pi->unscannedBitmap == 0) arena->unscannedPages &= ~((jsuword)1 << pageIndex); thingOffset = (pageGap + chunkIndex * thingsPerUnscannedChunk * thingSize); JS_ASSERT(thingOffset >= sizeof(JSGCPageInfo)); thingLimit = thingOffset + thingsPerUnscannedChunk * thingSize; if (thingsPerUnscannedChunk != 1) { /* * thingLimit can go beyond the last allocated thing for the * last chunk as the real limit can be inside the chunk. */ if (arena->list->last == arena && arena->list->lastLimit < (pageIndex * GC_PAGE_SIZE + thingLimit)) { thingLimit = (arena->list->lastLimit - pageIndex * GC_PAGE_SIZE); } else if (thingLimit > GC_PAGE_SIZE) { thingLimit = GC_PAGE_SIZE; } JS_ASSERT(thingLimit > thingOffset); } JS_ASSERT(arena->list->last != arena || arena->list->lastLimit >= (pageIndex * GC_PAGE_SIZE + thingLimit)); JS_ASSERT(thingLimit <= GC_PAGE_SIZE); for (; thingOffset != thingLimit; thingOffset += thingSize) { /* * XXX: inline js_GetGCThingFlags() to use already available * pi. */ thing = (void *)((jsuword)pi + thingOffset); flagp = js_GetGCThingFlags(thing); if (thingsPerUnscannedChunk != 1) { /* * Skip free or already scanned things that share the chunk * with unscanned ones. */ if ((*flagp & (GCF_MARK|GCF_FINAL)) != (GCF_MARK|GCF_FINAL)) continue; } JS_ASSERT((*flagp & (GCF_MARK|GCF_FINAL)) == (GCF_MARK|GCF_FINAL)); *flagp &= ~GCF_FINAL; #ifdef DEBUG JS_ASSERT(rt->gcUnscannedBagSize != 0); --rt->gcUnscannedBagSize; /* * Check that GC thing type is consistent with the type of * things that can be put to the unscanned bag. */ switch (*flagp & GCF_TYPEMASK) { case GCX_OBJECT: # if JS_HAS_XML_SUPPORT case GCX_NAMESPACE: case GCX_QNAME: case GCX_XML: # endif break; default: JS_ASSERT(0); } #endif MarkGCThingChildren(cx, thing, flagp, JS_FALSE); } } /* * We finished scanning of the arena but we can only pop it from * the stack if the arena is the stack's top. * * When MarkGCThingChildren from the above calls * AddThingToUnscannedBag and the latter pushes new arenas to the * stack, we have to skip popping of this arena until it becomes * the top of the stack again. */ if (arena == rt->gcUnscannedArenaStackTop) { prevArena = arena->prevUnscanned; arena->prevUnscanned = NULL; if (arena == prevArena) { /* * prevUnscanned points to itself and we reached the bottom * of the stack. */ break; } rt->gcUnscannedArenaStackTop = arena = prevArena; } else { arena = rt->gcUnscannedArenaStackTop; } if (arena->list->thingSize != thingSize) goto init_size; } JS_ASSERT(rt->gcUnscannedArenaStackTop); JS_ASSERT(!rt->gcUnscannedArenaStackTop->prevUnscanned); rt->gcUnscannedArenaStackTop = NULL; JS_ASSERT(rt->gcUnscannedBagSize == 0); } void js_MarkGCThing(JSContext *cx, void *thing) { uint8 *flagp; if (!thing) return; flagp = js_GetGCThingFlags(thing); JS_ASSERT(*flagp != GCF_FINAL); if (*flagp & GCF_MARK) return; *flagp |= GCF_MARK; if (!cx->insideGCMarkCallback) { MarkGCThingChildren(cx, thing, flagp, JS_TRUE); } else { /* * For API compatibility we allow for the callback to assume that * after it calls js_MarkGCThing for the last time, the callback * can start to finalize its own objects that are only referenced * by unmarked GC things. * * Since we do not know which call from inside the callback is the * last, we ensure that the unscanned bag is always empty when we * return to the callback and all marked things are scanned. * * As an optimization we do not check for the stack size here and * pass JS_FALSE as the last argument to MarkGCThingChildren. * Otherwise with low C stack the thing would be pushed to the bag * just to be feed to MarkGCThingChildren from inside * ScanDelayedChildren. */ cx->insideGCMarkCallback = JS_FALSE; MarkGCThingChildren(cx, thing, flagp, JS_FALSE); ScanDelayedChildren(cx); cx->insideGCMarkCallback = JS_TRUE; } } JS_STATIC_DLL_CALLBACK(JSDHashOperator) gc_root_marker(JSDHashTable *table, JSDHashEntryHdr *hdr, uint32 num, void *arg) { JSGCRootHashEntry *rhe = (JSGCRootHashEntry *)hdr; jsval *rp = (jsval *)rhe->root; jsval v = *rp; /* Ignore null object and scalar values. */ if (!JSVAL_IS_NULL(v) && JSVAL_IS_GCTHING(v)) { JSContext *cx = (JSContext *)arg; #ifdef DEBUG JSBool root_points_to_gcArenaList = JS_FALSE; jsuword thing = (jsuword) JSVAL_TO_GCTHING(v); uintN i; JSGCArenaList *arenaList; JSGCArena *a; size_t limit; for (i = 0; i < GC_NUM_FREELISTS; i++) { arenaList = &cx->runtime->gcArenaList[i]; limit = arenaList->lastLimit; for (a = arenaList->last; a; a = a->prev) { if (thing - FIRST_THING_PAGE(a) < limit) { root_points_to_gcArenaList = JS_TRUE; break; } limit = GC_THINGS_SIZE; } } if (!root_points_to_gcArenaList && rhe->name) { fprintf(stderr, "JS API usage error: the address passed to JS_AddNamedRoot currently holds an\n" "invalid jsval. This is usually caused by a missing call to JS_RemoveRoot.\n" "The root's name is \"%s\".\n", rhe->name); } JS_ASSERT(root_points_to_gcArenaList); #endif GC_MARK(cx, JSVAL_TO_GCTHING(v), rhe->name ? rhe->name : "root"); } return JS_DHASH_NEXT; } JS_STATIC_DLL_CALLBACK(JSDHashOperator) gc_lock_marker(JSDHashTable *table, JSDHashEntryHdr *hdr, uint32 num, void *arg) { JSGCLockHashEntry *lhe = (JSGCLockHashEntry *)hdr; void *thing = (void *)lhe->thing; JSContext *cx = (JSContext *)arg; GC_MARK(cx, thing, "locked object"); return JS_DHASH_NEXT; } #define GC_MARK_JSVALS(cx, len, vec, name) \ JS_BEGIN_MACRO \ jsval _v, *_vp, *_end; \ \ for (_vp = vec, _end = _vp + len; _vp < _end; _vp++) { \ _v = *_vp; \ if (JSVAL_IS_GCTHING(_v)) \ GC_MARK(cx, JSVAL_TO_GCTHING(_v), name); \ } \ JS_END_MACRO void js_MarkStackFrame(JSContext *cx, JSStackFrame *fp) { uintN depth, nslots; if (fp->callobj) GC_MARK(cx, fp->callobj, "call object"); if (fp->argsobj) GC_MARK(cx, fp->argsobj, "arguments object"); if (fp->varobj) GC_MARK(cx, fp->varobj, "variables object"); if (fp->script) { js_MarkScript(cx, fp->script); if (fp->spbase) { /* * Don't mark what has not been pushed yet, or what has been * popped already. */ depth = fp->script->depth; nslots = (JS_UPTRDIFF(fp->sp, fp->spbase) < depth * sizeof(jsval)) ? (uintN)(fp->sp - fp->spbase) : depth; GC_MARK_JSVALS(cx, nslots, fp->spbase, "operand"); } } /* Allow for primitive this parameter due to JSFUN_THISP_* flags. */ JS_ASSERT(JSVAL_IS_OBJECT((jsval)fp->thisp) || (fp->fun && JSFUN_THISP_FLAGS(fp->fun->flags))); if (JSVAL_IS_GCTHING((jsval)fp->thisp)) GC_MARK(cx, JSVAL_TO_GCTHING((jsval)fp->thisp), "this"); /* * Mark fp->argv, even though in the common case it will be marked via our * caller's frame, or via a JSStackHeader if fp was pushed by an external * invocation. * * The hard case is when there is not enough contiguous space in the stack * arena for actual, missing formal, and local root (JSFunctionSpec.extra) * slots. In this case, fp->argv points to new space in a new arena, and * marking the caller's operand stack, or an external caller's allocated * stack tracked by a JSStackHeader, will not mark all the values stored * and addressable via fp->argv. * * So in summary, solely for the hard case of moving argv due to missing * formals and extra roots, we must mark actuals, missing formals, and any * local roots arrayed at fp->argv here. * * It would be good to avoid redundant marking of the same reference, in * the case where fp->argv does point into caller-allocated space tracked * by fp->down->spbase or cx->stackHeaders. This would allow callbacks * such as the forthcoming rt->gcThingCallback (bug 333078) to compute JS * reference counts. So this comment deserves a FIXME bug to cite. */ if (fp->argv) { nslots = fp->argc; if (fp->fun) { if (fp->fun->nargs > nslots) nslots = fp->fun->nargs; if (!FUN_INTERPRETED(fp->fun)) nslots += fp->fun->u.n.extra; } GC_MARK_JSVALS(cx, nslots + 2, fp->argv - 2, "arg"); } if (JSVAL_IS_GCTHING(fp->rval)) GC_MARK(cx, JSVAL_TO_GCTHING(fp->rval), "rval"); if (fp->vars) GC_MARK_JSVALS(cx, fp->nvars, fp->vars, "var"); GC_MARK(cx, fp->scopeChain, "scope chain"); if (fp->sharpArray) GC_MARK(cx, fp->sharpArray, "sharp array"); if (fp->xmlNamespace) GC_MARK(cx, fp->xmlNamespace, "xmlNamespace"); } static void MarkWeakRoots(JSContext *cx, JSWeakRoots *wr) { uintN i; void *thing; for (i = 0; i < GCX_NTYPES; i++) GC_MARK(cx, wr->newborn[i], gc_typenames[i]); if (wr->lastAtom) GC_MARK_ATOM(cx, wr->lastAtom); if (JSVAL_IS_GCTHING(wr->lastInternalResult)) { thing = JSVAL_TO_GCTHING(wr->lastInternalResult); if (thing) GC_MARK(cx, thing, "lastInternalResult"); } } /* * When gckind is GC_LAST_DITCH, it indicates a call from js_NewGCThing with * rt->gcLock already held and when the lock should be kept on return. */ void js_GC(JSContext *cx, JSGCInvocationKind gckind) { JSRuntime *rt; JSBool keepAtoms; uintN i, type; JSContext *iter, *acx; #if JS_HAS_GENERATORS JSGenerator **genTodoTail; #endif JSStackFrame *fp, *chain; JSStackHeader *sh; JSTempValueRooter *tvr; size_t nbytes, limit, offset; JSGCArena *a, **ap; uint8 flags, *flagp, *firstPage; JSGCThing *thing, *freeList; JSGCArenaList *arenaList; GCFinalizeOp finalizer; JSBool allClear; #ifdef JS_THREADSAFE uint32 requestDebit; #endif rt = cx->runtime; #ifdef JS_THREADSAFE /* Avoid deadlock. */ JS_ASSERT(!JS_IS_RUNTIME_LOCKED(rt)); #endif if (gckind == GC_LAST_DITCH) { /* The last ditch GC preserves all atoms and weak roots. */ keepAtoms = JS_TRUE; } else { JS_CLEAR_WEAK_ROOTS(&cx->weakRoots); rt->gcPoke = JS_TRUE; /* Keep atoms when a suspended compile is running on another context. */ keepAtoms = (rt->gcKeepAtoms != 0); } /* * Don't collect garbage if the runtime isn't up, and cx is not the last * context in the runtime. The last context must force a GC, and nothing * should suppress that final collection or there may be shutdown leaks, * or runtime bloat until the next context is created. */ if (rt->state != JSRTS_UP && gckind != GC_LAST_CONTEXT) return; restart_after_callback: /* * Let the API user decide to defer a GC if it wants to (unless this * is the last context). Invoke the callback regardless. */ if (rt->gcCallback && !rt->gcCallback(cx, JSGC_BEGIN) && gckind != GC_LAST_CONTEXT) { return; } /* Lock out other GC allocator and collector invocations. */ if (gckind != GC_LAST_DITCH) JS_LOCK_GC(rt); /* Do nothing if no mutator has executed since the last GC. */ if (!rt->gcPoke) { METER(rt->gcStats.nopoke++); if (gckind != GC_LAST_DITCH) JS_UNLOCK_GC(rt); return; } METER(rt->gcStats.poke++); rt->gcPoke = JS_FALSE; #ifdef JS_THREADSAFE JS_ASSERT(cx->thread->id == js_CurrentThreadId()); /* Bump gcLevel and return rather than nest on this thread. */ if (rt->gcThread == cx->thread) { JS_ASSERT(rt->gcLevel > 0); rt->gcLevel++; METER(if (rt->gcLevel > rt->gcStats.maxlevel) rt->gcStats.maxlevel = rt->gcLevel); if (gckind != GC_LAST_DITCH) JS_UNLOCK_GC(rt); return; } /* * If we're in one or more requests (possibly on more than one context) * running on the current thread, indicate, temporarily, that all these * requests are inactive. If cx->thread is NULL, then cx is not using * the request model, and does not contribute to rt->requestCount. */ requestDebit = 0; if (cx->thread) { JSCList *head, *link; /* * Check all contexts on cx->thread->contextList for active requests, * counting each such context against requestDebit. */ head = &cx->thread->contextList; for (link = head->next; link != head; link = link->next) { acx = CX_FROM_THREAD_LINKS(link); JS_ASSERT(acx->thread == cx->thread); if (acx->requestDepth) requestDebit++; } } else { /* * We assert, but check anyway, in case someone is misusing the API. * Avoiding the loop over all of rt's contexts is a win in the event * that the GC runs only on request-less contexts with null threads, * in a special thread such as might be used by the UI/DOM/Layout * "mozilla" or "main" thread in Mozilla-the-browser. */ JS_ASSERT(cx->requestDepth == 0); if (cx->requestDepth) requestDebit = 1; } if (requestDebit) { JS_ASSERT(requestDebit <= rt->requestCount); rt->requestCount -= requestDebit; if (rt->requestCount == 0) JS_NOTIFY_REQUEST_DONE(rt); } /* If another thread is already in GC, don't attempt GC; wait instead. */ if (rt->gcLevel > 0) { /* Bump gcLevel to restart the current GC, so it finds new garbage. */ rt->gcLevel++; METER(if (rt->gcLevel > rt->gcStats.maxlevel) rt->gcStats.maxlevel = rt->gcLevel); /* Wait for the other thread to finish, then resume our request. */ while (rt->gcLevel > 0) JS_AWAIT_GC_DONE(rt); if (requestDebit) rt->requestCount += requestDebit; if (gckind != GC_LAST_DITCH) JS_UNLOCK_GC(rt); return; } /* No other thread is in GC, so indicate that we're now in GC. */ rt->gcLevel = 1; rt->gcThread = cx->thread; /* Wait for all other requests to finish. */ while (rt->requestCount > 0) JS_AWAIT_REQUEST_DONE(rt); #else /* !JS_THREADSAFE */ /* Bump gcLevel and return rather than nest; the outer gc will restart. */ rt->gcLevel++; METER(if (rt->gcLevel > rt->gcStats.maxlevel) rt->gcStats.maxlevel = rt->gcLevel); if (rt->gcLevel > 1) return; #endif /* !JS_THREADSAFE */ /* * Set rt->gcRunning here within the GC lock, and after waiting for any * active requests to end, so that new requests that try to JS_AddRoot, * JS_RemoveRoot, or JS_RemoveRootRT block in JS_BeginRequest waiting for * rt->gcLevel to drop to zero, while request-less calls to the *Root* * APIs block in js_AddRoot or js_RemoveRoot (see above in this file), * waiting for GC to finish. */ rt->gcRunning = JS_TRUE; JS_UNLOCK_GC(rt); /* Reset malloc counter. */ rt->gcMallocBytes = 0; /* Drop atoms held by the property cache, and clear property weak links. */ js_DisablePropertyCache(cx); js_FlushPropertyCache(cx); #ifdef DEBUG_scopemeters { extern void js_DumpScopeMeters(JSRuntime *rt); js_DumpScopeMeters(rt); } #endif #ifdef JS_THREADSAFE /* * Set all thread local freelists to NULL. We may visit a thread's * freelist more than once. To avoid redundant clearing we unroll the * current thread's step. * * Also, in case a JSScript wrapped within an object was finalized, we * null acx->thread->gsnCache.script and finish the cache's hashtable. * Note that js_DestroyScript, called from script_finalize, will have * already cleared cx->thread->gsnCache above during finalization, so we * don't have to here. */ memset(cx->thread->gcFreeLists, 0, sizeof cx->thread->gcFreeLists); iter = NULL; while ((acx = js_ContextIterator(rt, JS_FALSE, &iter)) != NULL) { if (!acx->thread || acx->thread == cx->thread) continue; memset(acx->thread->gcFreeLists, 0, sizeof acx->thread->gcFreeLists); GSN_CACHE_CLEAR(&acx->thread->gsnCache); } #else /* The thread-unsafe case just has to clear the runtime's GSN cache. */ GSN_CACHE_CLEAR(&rt->gsnCache); #endif restart: rt->gcNumber++; JS_ASSERT(!rt->gcUnscannedArenaStackTop); JS_ASSERT(rt->gcUnscannedBagSize == 0); /* * Mark phase. */ JS_DHashTableEnumerate(&rt->gcRootsHash, gc_root_marker, cx); if (rt->gcLocksHash) JS_DHashTableEnumerate(rt->gcLocksHash, gc_lock_marker, cx); js_MarkAtomState(&rt->atomState, keepAtoms, gc_mark_atom_key_thing, cx); js_MarkWatchPoints(cx); js_MarkScriptFilenames(rt, keepAtoms); js_MarkNativeIteratorStates(cx); #if JS_HAS_GENERATORS genTodoTail = MarkScheduledGenerators(cx); JS_ASSERT(!*genTodoTail); #endif iter = NULL; while ((acx = js_ContextIterator(rt, JS_TRUE, &iter)) != NULL) { /* * Iterate frame chain and dormant chains. Temporarily tack current * frame onto the head of the dormant list to ease iteration. * * (NB: see comment on this whole "dormant" thing in js_Execute.) */ chain = acx->fp; if (chain) { JS_ASSERT(!chain->dormantNext); chain->dormantNext = acx->dormantFrameChain; } else { chain = acx->dormantFrameChain; } for (fp = chain; fp; fp = chain = chain->dormantNext) { do { js_MarkStackFrame(cx, fp); } while ((fp = fp->down) != NULL); } /* Cleanup temporary "dormant" linkage. */ if (acx->fp) acx->fp->dormantNext = NULL; /* Mark other roots-by-definition in acx. */ GC_MARK(cx, acx->globalObject, "global object"); MarkWeakRoots(cx, &acx->weakRoots); if (acx->throwing) { if (JSVAL_IS_GCTHING(acx->exception)) GC_MARK(cx, JSVAL_TO_GCTHING(acx->exception), "exception"); } else { /* Avoid keeping GC-ed junk stored in JSContext.exception. */ acx->exception = JSVAL_NULL; } #if JS_HAS_LVALUE_RETURN if (acx->rval2set && JSVAL_IS_GCTHING(acx->rval2)) GC_MARK(cx, JSVAL_TO_GCTHING(acx->rval2), "rval2"); #endif for (sh = acx->stackHeaders; sh; sh = sh->down) { METER(rt->gcStats.stackseg++); METER(rt->gcStats.segslots += sh->nslots); GC_MARK_JSVALS(cx, sh->nslots, JS_STACK_SEGMENT(sh), "stack"); } if (acx->localRootStack) js_MarkLocalRoots(cx, acx->localRootStack); for (tvr = acx->tempValueRooters; tvr; tvr = tvr->down) { switch (tvr->count) { case JSTVU_SINGLE: if (JSVAL_IS_GCTHING(tvr->u.value)) { GC_MARK(cx, JSVAL_TO_GCTHING(tvr->u.value), "tvr->u.value"); } break; case JSTVU_MARKER: tvr->u.marker(cx, tvr); break; case JSTVU_SPROP: MARK_SCOPE_PROPERTY(cx, tvr->u.sprop); break; case JSTVU_WEAK_ROOTS: MarkWeakRoots(cx, tvr->u.weakRoots); break; default: JS_ASSERT(tvr->count >= 0); GC_MARK_JSVALS(cx, tvr->count, tvr->u.array, "tvr->u.array"); } } if (acx->sharpObjectMap.depth > 0) js_GCMarkSharpMap(cx, &acx->sharpObjectMap); } #ifdef DUMP_CALL_TABLE js_DumpCallTable(cx); #endif /* * Mark children of things that caused too deep recursion during above * marking phase. */ ScanDelayedChildren(cx); #if JS_HAS_GENERATORS /* * Close phase: search and mark part. See comments in * FindAndMarkObjectsToClose for details. */ FindAndMarkObjectsToClose(cx, gckind, genTodoTail); /* * Mark children of things that caused too deep recursion during the * just-completed marking part of the close phase. */ ScanDelayedChildren(cx); #endif JS_ASSERT(!cx->insideGCMarkCallback); if (rt->gcCallback) { cx->insideGCMarkCallback = JS_TRUE; (void) rt->gcCallback(cx, JSGC_MARK_END); JS_ASSERT(cx->insideGCMarkCallback); cx->insideGCMarkCallback = JS_FALSE; } JS_ASSERT(rt->gcUnscannedBagSize == 0); /* Finalize iterator states before the objects they iterate over. */ CloseIteratorStates(cx); /* * Sweep phase. * * Finalize as we sweep, outside of rt->gcLock but with rt->gcRunning set * so that any attempt to allocate a GC-thing from a finalizer will fail, * rather than nest badly and leave the unmarked newborn to be swept. * * Finalize smaller objects before larger, to guarantee finalization of * GC-allocated obj->slots after obj. See FreeSlots in jsobj.c. */ for (i = 0; i < GC_NUM_FREELISTS; i++) { arenaList = &rt->gcArenaList[i]; nbytes = GC_FREELIST_NBYTES(i); limit = arenaList->lastLimit; for (a = arenaList->last; a; a = a->prev) { JS_ASSERT(!a->prevUnscanned); JS_ASSERT(a->unscannedPages == 0); firstPage = (uint8 *) FIRST_THING_PAGE(a); for (offset = 0; offset != limit; offset += nbytes) { if ((offset & GC_PAGE_MASK) == 0) { JS_ASSERT(((JSGCPageInfo *)(firstPage + offset))-> unscannedBitmap == 0); offset += PAGE_THING_GAP(nbytes); } JS_ASSERT(offset < limit); flagp = a->base + offset / sizeof(JSGCThing); if (flagp >= firstPage) flagp += GC_THINGS_SIZE; flags = *flagp; if (flags & GCF_MARK) { *flagp &= ~GCF_MARK; } else if (!(flags & (GCF_LOCK | GCF_FINAL))) { /* Call the finalizer with GCF_FINAL ORed into flags. */ type = flags & GCF_TYPEMASK; finalizer = gc_finalizers[type]; if (finalizer) { thing = (JSGCThing *)(firstPage + offset); *flagp = (uint8)(flags | GCF_FINAL); if (type >= GCX_EXTERNAL_STRING) js_PurgeDeflatedStringCache(rt, (JSString *)thing); finalizer(cx, thing); } /* Set flags to GCF_FINAL, signifying that thing is free. */ *flagp = GCF_FINAL; } } limit = GC_THINGS_SIZE; } } /* * Sweep the runtime's property tree after finalizing objects, in case any * had watchpoints referencing tree nodes. Then sweep atoms, which may be * referenced from dead property ids. */ js_SweepScopeProperties(rt); js_SweepAtomState(&rt->atomState); /* * Sweep script filenames after sweeping functions in the generic loop * above. In this way when a scripted function's finalizer destroys the * script and calls rt->destroyScriptHook, the hook can still access the * script's filename. See bug 323267. */ js_SweepScriptFilenames(rt); /* * Free phase. * Free any unused arenas and rebuild the JSGCThing freelist. */ for (i = 0; i < GC_NUM_FREELISTS; i++) { arenaList = &rt->gcArenaList[i]; ap = &arenaList->last; a = *ap; if (!a) continue; allClear = JS_TRUE; arenaList->freeList = NULL; freeList = NULL; METER(arenaList->stats.nthings = 0); METER(arenaList->stats.freelen = 0); nbytes = GC_FREELIST_NBYTES(i); limit = arenaList->lastLimit; do { METER(size_t nfree = 0); firstPage = (uint8 *) FIRST_THING_PAGE(a); for (offset = 0; offset != limit; offset += nbytes) { if ((offset & GC_PAGE_MASK) == 0) offset += PAGE_THING_GAP(nbytes); JS_ASSERT(offset < limit); flagp = a->base + offset / sizeof(JSGCThing); if (flagp >= firstPage) flagp += GC_THINGS_SIZE; if (*flagp != GCF_FINAL) { allClear = JS_FALSE; METER(++arenaList->stats.nthings); } else { thing = (JSGCThing *)(firstPage + offset); thing->flagp = flagp; thing->next = freeList; freeList = thing; METER(++nfree); } } if (allClear) { /* * Forget just assembled free list head for the arena * and destroy the arena itself. */ freeList = arenaList->freeList; DestroyGCArena(rt, arenaList, ap); } else { allClear = JS_TRUE; arenaList->freeList = freeList; ap = &a->prev; METER(arenaList->stats.freelen += nfree); METER(arenaList->stats.totalfreelen += nfree); METER(++arenaList->stats.totalarenas); } limit = GC_THINGS_SIZE; } while ((a = *ap) != NULL); } if (rt->gcCallback) (void) rt->gcCallback(cx, JSGC_FINALIZE_END); #ifdef DEBUG_srcnotesize { extern void DumpSrcNoteSizeHist(); DumpSrcNoteSizeHist(); printf("GC HEAP SIZE %lu (%lu)\n", (unsigned long)rt->gcBytes, (unsigned long)rt->gcPrivateBytes); } #endif JS_LOCK_GC(rt); /* * We want to restart GC if js_GC was called recursively or if any of the * finalizers called js_RemoveRoot or js_UnlockGCThingRT. */ if (rt->gcLevel > 1 || rt->gcPoke) { rt->gcLevel = 1; rt->gcPoke = JS_FALSE; JS_UNLOCK_GC(rt); goto restart; } js_EnablePropertyCache(cx); rt->gcLevel = 0; rt->gcLastBytes = rt->gcBytes; rt->gcRunning = JS_FALSE; #ifdef JS_THREADSAFE /* If we were invoked during a request, pay back the temporary debit. */ if (requestDebit) rt->requestCount += requestDebit; rt->gcThread = NULL; JS_NOTIFY_GC_DONE(rt); /* * Unlock unless we have GC_LAST_DITCH which requires locked GC on return. */ if (gckind != GC_LAST_DITCH) JS_UNLOCK_GC(rt); #endif /* Execute JSGC_END callback outside the lock. */ if (rt->gcCallback) { JSWeakRoots savedWeakRoots; JSTempValueRooter tvr; if (gckind == GC_LAST_DITCH) { /* * We allow JSGC_END implementation to force a full GC or allocate * new GC things. Thus we must protect the weak roots from GC or * overwrites. */ savedWeakRoots = cx->weakRoots; JS_PUSH_TEMP_ROOT_WEAK_COPY(cx, &savedWeakRoots, &tvr); JS_KEEP_ATOMS(rt); JS_UNLOCK_GC(rt); } (void) rt->gcCallback(cx, JSGC_END); if (gckind == GC_LAST_DITCH) { JS_LOCK_GC(rt); JS_UNKEEP_ATOMS(rt); JS_POP_TEMP_ROOT(cx, &tvr); } else if (gckind == GC_LAST_CONTEXT && rt->gcPoke) { /* * On shutdown iterate until JSGC_END callback stops creating * garbage. */ goto restart_after_callback; } } } void js_UpdateMallocCounter(JSContext *cx, size_t nbytes) { uint32 *pbytes, bytes; #ifdef JS_THREADSAFE pbytes = &cx->thread->gcMallocBytes; #else pbytes = &cx->runtime->gcMallocBytes; #endif bytes = *pbytes; *pbytes = ((uint32)-1 - bytes <= nbytes) ? (uint32)-1 : bytes + nbytes; } pacparser-1.4.5/src/spidermonkey/js/src/jsgc.h000066400000000000000000000276061464010763600213250ustar00rootroot00000000000000/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- * * ***** BEGIN LICENSE BLOCK ***** * Version: MPL 1.1/GPL 2.0/LGPL 2.1 * * The contents of this file are subject to the Mozilla Public License Version * 1.1 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * http://www.mozilla.org/MPL/ * * Software distributed under the License is distributed on an "AS IS" basis, * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License * for the specific language governing rights and limitations under the * License. * * The Original Code is Mozilla Communicator client code, released * March 31, 1998. * * The Initial Developer of the Original Code is * Netscape Communications Corporation. * Portions created by the Initial Developer are Copyright (C) 1998 * the Initial Developer. All Rights Reserved. * * Contributor(s): * * Alternatively, the contents of this file may be used under the terms of * either of the GNU General Public License Version 2 or later (the "GPL"), * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), * in which case the provisions of the GPL or the LGPL are applicable instead * of those above. If you wish to allow use of your version of this file only * under the terms of either the GPL or the LGPL, and not to allow others to * use your version of this file under the terms of the MPL, indicate your * decision by deleting the provisions above and replace them with the notice * and other provisions required by the GPL or the LGPL. If you do not delete * the provisions above, a recipient may use your version of this file under * the terms of any one of the MPL, the GPL or the LGPL. * * ***** END LICENSE BLOCK ***** */ #ifndef jsgc_h___ #define jsgc_h___ /* * JS Garbage Collector. */ #include "jsprvtd.h" #include "jspubtd.h" #include "jsdhash.h" #include "jsutil.h" JS_BEGIN_EXTERN_C /* GC thing type indexes. */ #define GCX_OBJECT 0 /* JSObject */ #define GCX_STRING 1 /* JSString */ #define GCX_DOUBLE 2 /* jsdouble */ #define GCX_MUTABLE_STRING 3 /* JSString that's mutable -- single-threaded only! */ #define GCX_PRIVATE 4 /* private (unscanned) data */ #define GCX_NAMESPACE 5 /* JSXMLNamespace */ #define GCX_QNAME 6 /* JSXMLQName */ #define GCX_XML 7 /* JSXML */ #define GCX_EXTERNAL_STRING 8 /* JSString w/ external chars */ #define GCX_NTYPES_LOG2 4 /* type index bits */ #define GCX_NTYPES JS_BIT(GCX_NTYPES_LOG2) /* GC flag definitions, must fit in 8 bits (type index goes in the low bits). */ #define GCF_TYPEMASK JS_BITMASK(GCX_NTYPES_LOG2) #define GCF_MARK JS_BIT(GCX_NTYPES_LOG2) #define GCF_FINAL JS_BIT(GCX_NTYPES_LOG2 + 1) #define GCF_SYSTEM JS_BIT(GCX_NTYPES_LOG2 + 2) #define GCF_LOCKSHIFT (GCX_NTYPES_LOG2 + 3) /* lock bit shift */ #define GCF_LOCK JS_BIT(GCF_LOCKSHIFT) /* lock request bit in API */ /* Pseudo-flag that modifies GCX_STRING to make GCX_MUTABLE_STRING. */ #define GCF_MUTABLE 2 #if (GCX_STRING | GCF_MUTABLE) != GCX_MUTABLE_STRING # error "mutable string type index botch!" #endif extern uint8 * js_GetGCThingFlags(void *thing); /* * The sole purpose of the function is to preserve public API compatibility * in JS_GetStringBytes which takes only single JSString* argument. */ JSRuntime* js_GetGCStringRuntime(JSString *str); #if 1 /* * Since we're forcing a GC from JS_GC anyway, don't bother wasting cycles * loading oldval. XXX remove implied force, fix jsinterp.c's "second arg * ignored", etc. */ #define GC_POKE(cx, oldval) ((cx)->runtime->gcPoke = JS_TRUE) #else #define GC_POKE(cx, oldval) ((cx)->runtime->gcPoke = JSVAL_IS_GCTHING(oldval)) #endif extern intN js_ChangeExternalStringFinalizer(JSStringFinalizeOp oldop, JSStringFinalizeOp newop); extern JSBool js_InitGC(JSRuntime *rt, uint32 maxbytes); extern void js_FinishGC(JSRuntime *rt); extern JSBool js_AddRoot(JSContext *cx, void *rp, const char *name); extern JSBool js_AddRootRT(JSRuntime *rt, void *rp, const char *name); extern JSBool js_RemoveRoot(JSRuntime *rt, void *rp); #ifdef DEBUG extern void js_DumpNamedRoots(JSRuntime *rt, void (*dump)(const char *name, void *rp, void *data), void *data); #endif extern uint32 js_MapGCRoots(JSRuntime *rt, JSGCRootMapFun map, void *data); /* Table of pointers with count valid members. */ typedef struct JSPtrTable { size_t count; void **array; } JSPtrTable; extern JSBool js_RegisterCloseableIterator(JSContext *cx, JSObject *obj); #if JS_HAS_GENERATORS /* * Runtime state to support generators' close hooks. */ typedef struct JSGCCloseState { /* * Singly linked list of generators that are reachable from GC roots or * were created after the last GC. */ JSGenerator *reachableList; /* * Head of the queue of generators that have already become unreachable but * whose close hooks are not yet run. */ JSGenerator *todoQueue; #ifndef JS_THREADSAFE /* * Flag indicating that the current thread is excuting a close hook for * single thread case. */ JSBool runningCloseHook; #endif } JSGCCloseState; extern void js_RegisterGenerator(JSContext *cx, JSGenerator *gen); extern JSBool js_RunCloseHooks(JSContext *cx); #endif /* * The private JSGCThing struct, which describes a gcFreeList element. */ struct JSGCThing { JSGCThing *next; uint8 *flagp; }; #define GC_NBYTES_MAX (10 * sizeof(JSGCThing)) #define GC_NUM_FREELISTS (GC_NBYTES_MAX / sizeof(JSGCThing)) #define GC_FREELIST_NBYTES(i) (((i) + 1) * sizeof(JSGCThing)) #define GC_FREELIST_INDEX(n) (((n) / sizeof(JSGCThing)) - 1) extern void * js_NewGCThing(JSContext *cx, uintN flags, size_t nbytes); extern JSBool js_LockGCThing(JSContext *cx, void *thing); extern JSBool js_LockGCThingRT(JSRuntime *rt, void *thing); extern JSBool js_UnlockGCThingRT(JSRuntime *rt, void *thing); extern JSBool js_IsAboutToBeFinalized(JSContext *cx, void *thing); extern void js_MarkAtom(JSContext *cx, JSAtom *atom); /* We avoid a large number of unnecessary calls by doing the flag check first */ #define GC_MARK_ATOM(cx, atom) \ JS_BEGIN_MACRO \ if (!((atom)->flags & ATOM_MARK)) \ js_MarkAtom(cx, atom); \ JS_END_MACRO /* * Always use GC_MARK macro and never call js_MarkGCThing directly so * when GC_MARK_DEBUG is defined the dump of live GC things does not miss * a thing. */ extern void js_MarkGCThing(JSContext *cx, void *thing); #ifdef GC_MARK_DEBUG # define GC_MARK(cx, thing, name) js_MarkNamedGCThing(cx, thing, name) extern void js_MarkNamedGCThing(JSContext *cx, void *thing, const char *name); extern JS_FRIEND_DATA(FILE *) js_DumpGCHeap; JS_EXTERN_DATA(void *) js_LiveThingToFind; #else # define GC_MARK(cx, thing, name) js_MarkGCThing(cx, thing) #endif extern void js_MarkStackFrame(JSContext *cx, JSStackFrame *fp); /* * Kinds of js_GC invocation. */ typedef enum JSGCInvocationKind { /* Normal invocation. */ GC_NORMAL, /* * Called from js_DestroyContext for last JSContext in a JSRuntime, when * it is imperative that rt->gcPoke gets cleared early in js_GC. */ GC_LAST_CONTEXT, /* * Called from js_NewGCThing as a last-ditch GC attempt. See comments * before js_GC definition for details. */ GC_LAST_DITCH } JSGCInvocationKind; extern void js_GC(JSContext *cx, JSGCInvocationKind gckind); /* Call this after succesful malloc of memory for GC-related things. */ extern void js_UpdateMallocCounter(JSContext *cx, size_t nbytes); #ifdef DEBUG_notme #define JS_GCMETER 1 #endif #ifdef JS_GCMETER typedef struct JSGCStats { #ifdef JS_THREADSAFE uint32 localalloc; /* number of succeeded allocations from local lists */ #endif uint32 alloc; /* number of allocation attempts */ uint32 retry; /* allocation attempt retries after running the GC */ uint32 retryhalt; /* allocation retries halted by the branch callback */ uint32 fail; /* allocation failures */ uint32 finalfail; /* finalizer calls allocator failures */ uint32 lockborn; /* things born locked */ uint32 lock; /* valid lock calls */ uint32 unlock; /* valid unlock calls */ uint32 depth; /* mark tail recursion depth */ uint32 maxdepth; /* maximum mark tail recursion depth */ uint32 cdepth; /* mark recursion depth of C functions */ uint32 maxcdepth; /* maximum mark recursion depth of C functions */ uint32 unscanned; /* mark C stack overflows or number of times GC things were put in unscanned bag */ #ifdef DEBUG uint32 maxunscanned; /* maximum size of unscanned bag */ #endif uint32 maxlevel; /* maximum GC nesting (indirect recursion) level */ uint32 poke; /* number of potentially useful GC calls */ uint32 nopoke; /* useless GC calls where js_PokeGC was not set */ uint32 afree; /* thing arenas freed so far */ uint32 stackseg; /* total extraordinary stack segments scanned */ uint32 segslots; /* total stack segment jsval slots scanned */ uint32 nclose; /* number of objects with close hooks */ uint32 maxnclose; /* max number of objects with close hooks */ uint32 closelater; /* number of close hooks scheduled to run */ uint32 maxcloselater; /* max number of close hooks scheduled to run */ } JSGCStats; extern JS_FRIEND_API(void) js_DumpGCStats(JSRuntime *rt, FILE *fp); #endif /* JS_GCMETER */ typedef struct JSGCArena JSGCArena; typedef struct JSGCArenaList JSGCArenaList; #ifdef JS_GCMETER typedef struct JSGCArenaStats JSGCArenaStats; struct JSGCArenaStats { uint32 narenas; /* number of arena in list */ uint32 maxarenas; /* maximun number of allocated arenas */ uint32 nthings; /* number of allocates JSGCThing */ uint32 maxthings; /* maximum number number of allocates JSGCThing */ uint32 totalnew; /* number of succeeded calls to js_NewGCThing */ uint32 freelen; /* freeList lengths */ uint32 recycle; /* number of things recycled through freeList */ uint32 totalarenas; /* total number of arenas with live things that GC scanned so far */ uint32 totalfreelen; /* total number of things that GC put to free list so far */ }; #endif struct JSGCArenaList { JSGCArena *last; /* last allocated GC arena */ uint16 lastLimit; /* end offset of allocated so far things in the last arena */ uint16 thingSize; /* size of things to allocate on this list */ JSGCThing *freeList; /* list of free GC things */ #ifdef JS_GCMETER JSGCArenaStats stats; #endif }; typedef struct JSWeakRoots { /* Most recently created things by type, members of the GC's root set. */ JSGCThing *newborn[GCX_NTYPES]; /* Atom root for the last-looked-up atom on this context. */ JSAtom *lastAtom; /* Root for the result of the most recent js_InternalInvoke call. */ jsval lastInternalResult; } JSWeakRoots; JS_STATIC_ASSERT(JSVAL_NULL == 0); #define JS_CLEAR_WEAK_ROOTS(wr) (memset((wr), 0, sizeof(JSWeakRoots))) #ifdef DEBUG_notme #define TOO_MUCH_GC 1 #endif #ifdef WAY_TOO_MUCH_GC #define TOO_MUCH_GC 1 #endif JS_END_EXTERN_C #endif /* jsgc_h___ */ pacparser-1.4.5/src/spidermonkey/js/src/jshash.c000066400000000000000000000321421464010763600216410ustar00rootroot00000000000000/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- * * ***** BEGIN LICENSE BLOCK ***** * Version: MPL 1.1/GPL 2.0/LGPL 2.1 * * The contents of this file are subject to the Mozilla Public License Version * 1.1 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * http://www.mozilla.org/MPL/ * * Software distributed under the License is distributed on an "AS IS" basis, * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License * for the specific language governing rights and limitations under the * License. * * The Original Code is Mozilla Communicator client code, released * March 31, 1998. * * The Initial Developer of the Original Code is * Netscape Communications Corporation. * Portions created by the Initial Developer are Copyright (C) 1998 * the Initial Developer. All Rights Reserved. * * Contributor(s): * * Alternatively, the contents of this file may be used under the terms of * either of the GNU General Public License Version 2 or later (the "GPL"), * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), * in which case the provisions of the GPL or the LGPL are applicable instead * of those above. If you wish to allow use of your version of this file only * under the terms of either the GPL or the LGPL, and not to allow others to * use your version of this file under the terms of the MPL, indicate your * decision by deleting the provisions above and replace them with the notice * and other provisions required by the GPL or the LGPL. If you do not delete * the provisions above, a recipient may use your version of this file under * the terms of any one of the MPL, the GPL or the LGPL. * * ***** END LICENSE BLOCK ***** */ /* * PR hash table package. */ #include "jsstddef.h" #include #include #include "jstypes.h" #include "jsbit.h" #include "jsutil.h" /* Added by JSIFY */ #include "jshash.h" /* Added by JSIFY */ /* Compute the number of buckets in ht */ #define NBUCKETS(ht) JS_BIT(JS_HASH_BITS - (ht)->shift) /* The smallest table has 16 buckets */ #define MINBUCKETSLOG2 4 #define MINBUCKETS JS_BIT(MINBUCKETSLOG2) /* Compute the maximum entries given n buckets that we will tolerate, ~90% */ #define OVERLOADED(n) ((n) - ((n) >> 3)) /* Compute the number of entries below which we shrink the table by half */ #define UNDERLOADED(n) (((n) > MINBUCKETS) ? ((n) >> 2) : 0) /* ** Stubs for default hash allocator ops. */ static void * DefaultAllocTable(void *pool, size_t size) { return malloc(size); } static void DefaultFreeTable(void *pool, void *item) { free(item); } static JSHashEntry * DefaultAllocEntry(void *pool, const void *key) { return (JSHashEntry*) malloc(sizeof(JSHashEntry)); } static void DefaultFreeEntry(void *pool, JSHashEntry *he, uintN flag) { if (flag == HT_FREE_ENTRY) free(he); } static JSHashAllocOps defaultHashAllocOps = { DefaultAllocTable, DefaultFreeTable, DefaultAllocEntry, DefaultFreeEntry }; JS_PUBLIC_API(JSHashTable *) JS_NewHashTable(uint32 n, JSHashFunction keyHash, JSHashComparator keyCompare, JSHashComparator valueCompare, JSHashAllocOps *allocOps, void *allocPriv) { JSHashTable *ht; size_t nb; if (n <= MINBUCKETS) { n = MINBUCKETSLOG2; } else { n = JS_CeilingLog2(n); if ((int32)n < 0) return NULL; } if (!allocOps) allocOps = &defaultHashAllocOps; ht = (JSHashTable*) allocOps->allocTable(allocPriv, sizeof *ht); if (!ht) return NULL; memset(ht, 0, sizeof *ht); ht->shift = JS_HASH_BITS - n; n = JS_BIT(n); nb = n * sizeof(JSHashEntry *); ht->buckets = (JSHashEntry**) allocOps->allocTable(allocPriv, nb); if (!ht->buckets) { allocOps->freeTable(allocPriv, ht); return NULL; } memset(ht->buckets, 0, nb); ht->keyHash = keyHash; ht->keyCompare = keyCompare; ht->valueCompare = valueCompare; ht->allocOps = allocOps; ht->allocPriv = allocPriv; return ht; } JS_PUBLIC_API(void) JS_HashTableDestroy(JSHashTable *ht) { uint32 i, n; JSHashEntry *he, **hep; JSHashAllocOps *allocOps = ht->allocOps; void *allocPriv = ht->allocPriv; n = NBUCKETS(ht); for (i = 0; i < n; i++) { hep = &ht->buckets[i]; while ((he = *hep) != NULL) { *hep = he->next; allocOps->freeEntry(allocPriv, he, HT_FREE_ENTRY); } } #ifdef DEBUG memset(ht->buckets, 0xDB, n * sizeof ht->buckets[0]); #endif allocOps->freeTable(allocPriv, ht->buckets); #ifdef DEBUG memset(ht, 0xDB, sizeof *ht); #endif allocOps->freeTable(allocPriv, ht); } /* * Multiplicative hash, from Knuth 6.4. */ #define BUCKET_HEAD(ht, keyHash) \ (&(ht)->buckets[((keyHash) * JS_GOLDEN_RATIO) >> (ht)->shift]) JS_PUBLIC_API(JSHashEntry **) JS_HashTableRawLookup(JSHashTable *ht, JSHashNumber keyHash, const void *key) { JSHashEntry *he, **hep, **hep0; #ifdef HASHMETER ht->nlookups++; #endif hep = hep0 = BUCKET_HEAD(ht, keyHash); while ((he = *hep) != NULL) { if (he->keyHash == keyHash && ht->keyCompare(key, he->key)) { /* Move to front of chain if not already there */ if (hep != hep0) { *hep = he->next; he->next = *hep0; *hep0 = he; } return hep0; } hep = &he->next; #ifdef HASHMETER ht->nsteps++; #endif } return hep; } static JSBool Resize(JSHashTable *ht, uint32 newshift) { size_t nb, nentries, i; JSHashEntry **oldbuckets, *he, *next, **hep; #ifdef DEBUG size_t nold = NBUCKETS(ht); #endif JS_ASSERT(newshift < JS_HASH_BITS); nb = (size_t)1 << (JS_HASH_BITS - newshift); /* Integer overflow protection. */ if (nb > (size_t)-1 / sizeof(JSHashEntry*)) return JS_FALSE; nb *= sizeof(JSHashEntry*); oldbuckets = ht->buckets; ht->buckets = (JSHashEntry**)ht->allocOps->allocTable(ht->allocPriv, nb); if (!ht->buckets) { ht->buckets = oldbuckets; return JS_FALSE; } memset(ht->buckets, 0, nb); ht->shift = newshift; nentries = ht->nentries; for (i = 0; nentries != 0; i++) { for (he = oldbuckets[i]; he; he = next) { JS_ASSERT(nentries != 0); --nentries; next = he->next; hep = BUCKET_HEAD(ht, he->keyHash); /* * Since he comes from the old table, it must be unique and we * simply add it to the head of bucket chain without chain lookup. */ he->next = *hep; *hep = he; } } #ifdef DEBUG memset(oldbuckets, 0xDB, nold * sizeof oldbuckets[0]); #endif ht->allocOps->freeTable(ht->allocPriv, oldbuckets); return JS_TRUE; } JS_PUBLIC_API(JSHashEntry *) JS_HashTableRawAdd(JSHashTable *ht, JSHashEntry **hep, JSHashNumber keyHash, const void *key, void *value) { uint32 n; JSHashEntry *he; /* Grow the table if it is overloaded */ n = NBUCKETS(ht); if (ht->nentries >= OVERLOADED(n)) { if (!Resize(ht, ht->shift - 1)) return NULL; #ifdef HASHMETER ht->ngrows++; #endif hep = JS_HashTableRawLookup(ht, keyHash, key); } /* Make a new key value entry */ he = ht->allocOps->allocEntry(ht->allocPriv, key); if (!he) return NULL; he->keyHash = keyHash; he->key = key; he->value = value; he->next = *hep; *hep = he; ht->nentries++; return he; } JS_PUBLIC_API(JSHashEntry *) JS_HashTableAdd(JSHashTable *ht, const void *key, void *value) { JSHashNumber keyHash; JSHashEntry *he, **hep; keyHash = ht->keyHash(key); hep = JS_HashTableRawLookup(ht, keyHash, key); if ((he = *hep) != NULL) { /* Hit; see if values match */ if (ht->valueCompare(he->value, value)) { /* key,value pair is already present in table */ return he; } if (he->value) ht->allocOps->freeEntry(ht->allocPriv, he, HT_FREE_VALUE); he->value = value; return he; } return JS_HashTableRawAdd(ht, hep, keyHash, key, value); } JS_PUBLIC_API(void) JS_HashTableRawRemove(JSHashTable *ht, JSHashEntry **hep, JSHashEntry *he) { uint32 n; *hep = he->next; ht->allocOps->freeEntry(ht->allocPriv, he, HT_FREE_ENTRY); /* Shrink table if it's underloaded */ n = NBUCKETS(ht); if (--ht->nentries < UNDERLOADED(n)) { Resize(ht, ht->shift + 1); #ifdef HASHMETER ht->nshrinks++; #endif } } JS_PUBLIC_API(JSBool) JS_HashTableRemove(JSHashTable *ht, const void *key) { JSHashNumber keyHash; JSHashEntry *he, **hep; keyHash = ht->keyHash(key); hep = JS_HashTableRawLookup(ht, keyHash, key); if ((he = *hep) == NULL) return JS_FALSE; /* Hit; remove element */ JS_HashTableRawRemove(ht, hep, he); return JS_TRUE; } JS_PUBLIC_API(void *) JS_HashTableLookup(JSHashTable *ht, const void *key) { JSHashNumber keyHash; JSHashEntry *he, **hep; keyHash = ht->keyHash(key); hep = JS_HashTableRawLookup(ht, keyHash, key); if ((he = *hep) != NULL) { return he->value; } return NULL; } /* ** Iterate over the entries in the hash table calling func for each ** entry found. Stop if "f" says to (return value & JS_ENUMERATE_STOP). ** Return a count of the number of elements scanned. */ JS_PUBLIC_API(int) JS_HashTableEnumerateEntries(JSHashTable *ht, JSHashEnumerator f, void *arg) { JSHashEntry *he, **hep, **bucket; uint32 nlimit, n, nbuckets, newlog2; int rv; nlimit = ht->nentries; n = 0; for (bucket = ht->buckets; n != nlimit; ++bucket) { hep = bucket; while ((he = *hep) != NULL) { JS_ASSERT(n < nlimit); rv = f(he, n, arg); n++; if (rv & HT_ENUMERATE_REMOVE) { *hep = he->next; ht->allocOps->freeEntry(ht->allocPriv, he, HT_FREE_ENTRY); --ht->nentries; } else { hep = &he->next; } if (rv & HT_ENUMERATE_STOP) { goto out; } } } out: /* Shrink table if removal of entries made it underloaded */ if (ht->nentries != nlimit) { JS_ASSERT(ht->nentries < nlimit); nbuckets = NBUCKETS(ht); if (MINBUCKETS < nbuckets && ht->nentries < UNDERLOADED(nbuckets)) { newlog2 = JS_CeilingLog2(ht->nentries); if (newlog2 < MINBUCKETSLOG2) newlog2 = MINBUCKETSLOG2; /* Check that we really shrink the table. */ JS_ASSERT(JS_HASH_BITS - ht->shift > newlog2); Resize(ht, JS_HASH_BITS - newlog2); } } return (int)n; } #ifdef HASHMETER #include #include JS_PUBLIC_API(void) JS_HashTableDumpMeter(JSHashTable *ht, JSHashEnumerator dump, FILE *fp) { double sqsum, mean, variance, sigma; uint32 nchains, nbuckets, nentries; uint32 i, n, maxChain, maxChainLen; JSHashEntry *he; sqsum = 0; nchains = 0; maxChainLen = 0; nbuckets = NBUCKETS(ht); for (i = 0; i < nbuckets; i++) { he = ht->buckets[i]; if (!he) continue; nchains++; for (n = 0; he; he = he->next) n++; sqsum += n * n; if (n > maxChainLen) { maxChainLen = n; maxChain = i; } } nentries = ht->nentries; mean = (double)nentries / nchains; variance = nchains * sqsum - nentries * nentries; if (variance < 0 || nchains == 1) variance = 0; else variance /= nchains * (nchains - 1); sigma = sqrt(variance); fprintf(fp, "\nHash table statistics:\n"); fprintf(fp, " number of lookups: %u\n", ht->nlookups); fprintf(fp, " number of entries: %u\n", ht->nentries); fprintf(fp, " number of grows: %u\n", ht->ngrows); fprintf(fp, " number of shrinks: %u\n", ht->nshrinks); fprintf(fp, " mean steps per hash: %g\n", (double)ht->nsteps / ht->nlookups); fprintf(fp, "mean hash chain length: %g\n", mean); fprintf(fp, " standard deviation: %g\n", sigma); fprintf(fp, " max hash chain length: %u\n", maxChainLen); fprintf(fp, " max hash chain: [%u]\n", maxChain); for (he = ht->buckets[maxChain], i = 0; he; he = he->next, i++) if (dump(he, i, fp) != HT_ENUMERATE_NEXT) break; } #endif /* HASHMETER */ JS_PUBLIC_API(int) JS_HashTableDump(JSHashTable *ht, JSHashEnumerator dump, FILE *fp) { int count; count = JS_HashTableEnumerateEntries(ht, dump, fp); #ifdef HASHMETER JS_HashTableDumpMeter(ht, dump, fp); #endif return count; } JS_PUBLIC_API(JSHashNumber) JS_HashString(const void *key) { JSHashNumber h; const unsigned char *s; h = 0; for (s = (const unsigned char *)key; *s; s++) h = (h >> (JS_HASH_BITS - 4)) ^ (h << 4) ^ *s; return h; } JS_PUBLIC_API(int) JS_CompareValues(const void *v1, const void *v2) { return v1 == v2; } pacparser-1.4.5/src/spidermonkey/js/src/jshash.h000066400000000000000000000135201464010763600216450ustar00rootroot00000000000000/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- * * ***** BEGIN LICENSE BLOCK ***** * Version: MPL 1.1/GPL 2.0/LGPL 2.1 * * The contents of this file are subject to the Mozilla Public License Version * 1.1 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * http://www.mozilla.org/MPL/ * * Software distributed under the License is distributed on an "AS IS" basis, * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License * for the specific language governing rights and limitations under the * License. * * The Original Code is Mozilla Communicator client code, released * March 31, 1998. * * The Initial Developer of the Original Code is * Netscape Communications Corporation. * Portions created by the Initial Developer are Copyright (C) 1998 * the Initial Developer. All Rights Reserved. * * Contributor(s): * * Alternatively, the contents of this file may be used under the terms of * either of the GNU General Public License Version 2 or later (the "GPL"), * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), * in which case the provisions of the GPL or the LGPL are applicable instead * of those above. If you wish to allow use of your version of this file only * under the terms of either the GPL or the LGPL, and not to allow others to * use your version of this file under the terms of the MPL, indicate your * decision by deleting the provisions above and replace them with the notice * and other provisions required by the GPL or the LGPL. If you do not delete * the provisions above, a recipient may use your version of this file under * the terms of any one of the MPL, the GPL or the LGPL. * * ***** END LICENSE BLOCK ***** */ #ifndef jshash_h___ #define jshash_h___ /* * API to portable hash table code. */ #include #include #include "jstypes.h" #include "jscompat.h" JS_BEGIN_EXTERN_C typedef uint32 JSHashNumber; typedef struct JSHashEntry JSHashEntry; typedef struct JSHashTable JSHashTable; #define JS_HASH_BITS 32 #define JS_GOLDEN_RATIO 0x9E3779B9U typedef JSHashNumber (* JS_DLL_CALLBACK JSHashFunction)(const void *key); typedef intN (* JS_DLL_CALLBACK JSHashComparator)(const void *v1, const void *v2); typedef intN (* JS_DLL_CALLBACK JSHashEnumerator)(JSHashEntry *he, intN i, void *arg); /* Flag bits in JSHashEnumerator's return value */ #define HT_ENUMERATE_NEXT 0 /* continue enumerating entries */ #define HT_ENUMERATE_STOP 1 /* stop enumerating entries */ #define HT_ENUMERATE_REMOVE 2 /* remove and free the current entry */ typedef struct JSHashAllocOps { void * (*allocTable)(void *pool, size_t size); void (*freeTable)(void *pool, void *item); JSHashEntry * (*allocEntry)(void *pool, const void *key); void (*freeEntry)(void *pool, JSHashEntry *he, uintN flag); } JSHashAllocOps; #define HT_FREE_VALUE 0 /* just free the entry's value */ #define HT_FREE_ENTRY 1 /* free value and entire entry */ struct JSHashEntry { JSHashEntry *next; /* hash chain linkage */ JSHashNumber keyHash; /* key hash function result */ const void *key; /* ptr to opaque key */ void *value; /* ptr to opaque value */ }; struct JSHashTable { JSHashEntry **buckets; /* vector of hash buckets */ uint32 nentries; /* number of entries in table */ uint32 shift; /* multiplicative hash shift */ JSHashFunction keyHash; /* key hash function */ JSHashComparator keyCompare; /* key comparison function */ JSHashComparator valueCompare; /* value comparison function */ JSHashAllocOps *allocOps; /* allocation operations */ void *allocPriv; /* allocation private data */ #ifdef HASHMETER uint32 nlookups; /* total number of lookups */ uint32 nsteps; /* number of hash chains traversed */ uint32 ngrows; /* number of table expansions */ uint32 nshrinks; /* number of table contractions */ #endif }; /* * Create a new hash table. * If allocOps is null, use default allocator ops built on top of malloc(). */ extern JS_PUBLIC_API(JSHashTable *) JS_NewHashTable(uint32 n, JSHashFunction keyHash, JSHashComparator keyCompare, JSHashComparator valueCompare, JSHashAllocOps *allocOps, void *allocPriv); extern JS_PUBLIC_API(void) JS_HashTableDestroy(JSHashTable *ht); /* Low level access methods */ extern JS_PUBLIC_API(JSHashEntry **) JS_HashTableRawLookup(JSHashTable *ht, JSHashNumber keyHash, const void *key); extern JS_PUBLIC_API(JSHashEntry *) JS_HashTableRawAdd(JSHashTable *ht, JSHashEntry **hep, JSHashNumber keyHash, const void *key, void *value); extern JS_PUBLIC_API(void) JS_HashTableRawRemove(JSHashTable *ht, JSHashEntry **hep, JSHashEntry *he); /* Higher level access methods */ extern JS_PUBLIC_API(JSHashEntry *) JS_HashTableAdd(JSHashTable *ht, const void *key, void *value); extern JS_PUBLIC_API(JSBool) JS_HashTableRemove(JSHashTable *ht, const void *key); extern JS_PUBLIC_API(intN) JS_HashTableEnumerateEntries(JSHashTable *ht, JSHashEnumerator f, void *arg); extern JS_PUBLIC_API(void *) JS_HashTableLookup(JSHashTable *ht, const void *key); extern JS_PUBLIC_API(intN) JS_HashTableDump(JSHashTable *ht, JSHashEnumerator dump, FILE *fp); /* General-purpose C string hash function. */ extern JS_PUBLIC_API(JSHashNumber) JS_HashString(const void *key); /* Stub function just returns v1 == v2 */ extern JS_PUBLIC_API(intN) JS_CompareValues(const void *v1, const void *v2); JS_END_EXTERN_C #endif /* jshash_h___ */ pacparser-1.4.5/src/spidermonkey/js/src/jsify.pl000066400000000000000000000222141464010763600216750ustar00rootroot00000000000000#!/usr/local/bin/perl # This script modifies C code to use the hijacked NSPR routines that are # now baked into the JavaScript engine rather than using the NSPR # routines that they were based on, i.e. types like PRArenaPool are changed # to JSArenaPool. # # This script was used in 9/98 to facilitate the incorporation of some NSPR # code into the JS engine so as to minimize dependency on NSPR. # # Command-line: jsify.pl [options] [filename]* # # Options: # -r Reverse direction of transformation, i.e. JS ==> NSPR2 # -outdir Directory in which to place output files # NSPR2 symbols that will be modified to JS symbols, e.g. # PRArena <==> JSArena @NSPR_symbols = ( "PRArena", "PRArenaPool", "PRArenaStats", "PR_ARENAMETER", "PR_ARENA_", "PR_ARENA_ALIGN", "PR_ARENA_ALLOCATE", "PR_ARENA_CONST_ALIGN_MASK", "PR_ARENA_DEFAULT_ALIGN", "PR_ARENA_DESTROY", "PR_ARENA_GROW", "PR_ARENA_MARK", "PR_ARENA_RELEASE", "PR_smprintf", "PR_smprintf_free", "PR_snprintf", "PR_sprintf_append", "PR_sscanf", "PR_sxprintf", "PR_vsmprintf", "PR_vsnprintf", "PR_vsprintf_append", "PR_vsxprintf", "PRCList", "PRCListStr", "PRCLists", "PRDestroyEventProc", "PREvent", "PREventFunProc", "PREventQueue", "PRHandleEventProc", "PR_PostEvent", "PR_PostSynchronousEvent", "PR_ProcessPendingEvents", "PR_CreateEventQueue", "PR_DequeueEvent", "PR_DestroyEvent", "PR_DestroyEventQueue", "PR_EventAvailable", "PR_EventLoop", "PR_GetEvent", "PR_GetEventOwner", "PR_GetEventQueueMonitor", "PR_GetEventQueueSelectFD", "PR_GetMainEventQueue", "PR_HandleEvent", "PR_InitEvent", "PR_ENTER_EVENT_QUEUE_MONITOR", "PR_EXIT_EVENT_QUEUE_MONITOR", "PR_MapEvents", "PR_RevokeEvents", "PR_cnvtf", "PR_dtoa", "PR_strtod", "PRFileDesc", "PR_HASH_BITS", "PR_GOLDEN_RATIO", "PRHashAllocOps", "PRHashComparator", "PRHashEntry", "PRHashEnumerator", "PRHashFunction", "PRHashNumber", "PRHashTable", "PR_HashString", "PR_HashTableAdd", "PR_HashTableDestroy", "PR_HashTableDump", "PR_HashTableEnumerateEntries", "PR_HashTableLookup", "PR_HashTableRawAdd", "PR_HashTableRawLookup", "PR_HashTableRawRemove", "PR_HashTableRemove", "PRBool", "PRFloat64", "PRInt16", "PRInt32", "PRInt64", "PRInt8", "PRIntn", "PRUint16", "PRUint32", "PRUint64", "PRUint8", "PRUintn", "PRPtrDiff", "PRPtrdiff", "PRUptrdiff", "PRUword", "PRWord", "PRPackedBool", "PRSize", "PRStatus", "pruword", "prword", "prword_t", "PR_ALIGN_OF_DOUBLE", "PR_ALIGN_OF_FLOAT", "PR_ALIGN_OF_INT", "PR_ALIGN_OF_INT64", "PR_ALIGN_OF_LONG", "PR_ALIGN_OF_POINTER", "PR_ALIGN_OF_SHORT", "PR_ALIGN_OF_WORD", "PR_BITS_PER_BYTE", "PR_BITS_PER_BYTE_LOG2", "PR_BITS_PER_DOUBLE", "PR_BITS_PER_DOUBLE_LOG2", "PR_BITS_PER_FLOAT", "PR_BITS_PER_FLOAT_LOG2", "PR_BITS_PER_INT", "PR_BITS_PER_INT64", "PR_BITS_PER_INT64_LOG2", "PR_BITS_PER_INT_LOG2", "PR_BITS_PER_LONG", "PR_BITS_PER_LONG_LOG2", "PR_BITS_PER_SHORT", "PR_BITS_PER_SHORT_LOG2", "PR_BITS_PER_WORD", "PR_BITS_PER_WORD_LOG2", "PR_BYTES_PER_BYTE", "PR_BYTES_PER_DOUBLE", "PR_BYTES_PER_DWORD", "PR_BYTES_PER_DWORD_LOG2", "PR_BYTES_PER_FLOAT", "PR_BYTES_PER_INT", "PR_BYTES_PER_INT64", "PR_BYTES_PER_LONG", "PR_BYTES_PER_SHORT", "PR_BYTES_PER_WORD", "PR_BYTES_PER_WORD_LOG2", "PRSegment", "PRSegmentAccess", "PRStuffFunc", "PRThread", "PR_APPEND_LINK", "PR_ASSERT", "PR_ATOMIC_DWORD_LOAD", "PR_ATOMIC_DWORD_STORE", "PR_Abort", "PR_ArenaAllocate", "PR_ArenaCountAllocation", "PR_ArenaCountGrowth", "PR_ArenaCountInplaceGrowth", "PR_ArenaCountRelease", "PR_ArenaCountRetract", "PR_ArenaFinish", "PR_ArenaGrow", "PR_ArenaRelease", "PR_CompactArenaPool", "PR_DumpArenaStats", "PR_FinishArenaPool", "PR_FreeArenaPool", "PR_InitArenaPool", "PR_Assert", "PR_AttachThread", "PR_BEGIN_EXTERN_C", "PR_BEGIN_MACRO", "PR_BIT", "PR_BITMASK", "PR_BUFFER_OVERFLOW_ERROR", "PR_CALLBACK", "PR_CALLBACK_DECL", "PR_CALLOC", "PR_CEILING_LOG2", "PR_CLEAR_ARENA", "PR_CLEAR_BIT", "PR_CLEAR_UNUSED", "PR_CLIST_IS_EMPTY", "PR_COUNT_ARENA", "PR_CURRENT_THREAD", "PR_GetSegmentAccess", "PR_GetSegmentSize", "PR_GetSegmentVaddr", "PR_GrowSegment", "PR_DestroySegment", "PR_MapSegment", "PR_NewSegment", "PR_Segment", "PR_Seg", "PR_SEGMENT_NONE", "PR_SEGMENT_RDONLY", "PR_SEGMENT_RDWR", "PR_Calloc", "PR_CeilingLog2", "PR_CompareStrings", "PR_CompareValues", "PR_DELETE", "PR_END_EXTERN_C", "PR_END_MACRO", "PR_ENUMERATE_STOP", "PR_FAILURE", "PR_FALSE", "PR_FLOOR_LOG2", "PR_FREEIF", "PR_FREE_PATTERN", "PR_FloorLog2", "PR_FormatTime", "PR_Free", "PR_GetEnv", "PR_GetError", "PR_INIT_ARENA_POOL", "PR_INIT_CLIST", "PR_INIT_STATIC_CLIST", "PR_INLINE", "PR_INSERT_AFTER", "PR_INSERT_BEFORE", "PR_INSERT_LINK", "PR_INT32", "PR_INTERVAL_NO_TIMEOUT", "PR_INTERVAL_NO_WAIT", "PR_Init", "PR_LIST_HEAD", "PR_LIST_TAIL", "PR_LOG", "PR_LOGGING", "PR_LOG_ALWAYS", "PR_LOG_BEGIN", "PR_LOG_DEBUG", "PR_LOG_DEFINE", "PR_LOG_END", "PR_LOG_ERROR", "PR_LOG_MAX", "PR_LOG_MIN", "PR_LOG_NONE", "PR_LOG_NOTICE", "PR_LOG_TEST", "PR_LOG_WARN", "PR_LOG_WARNING", "PR_LogFlush", "PR_LogPrint", "PR_MALLOC", "PR_MAX", "PR_MD_calloc", "PR_MD_free", "PR_MD_malloc", "PR_MD_realloc", "PR_MIN", "PR_Malloc", "PR_NEW", "PR_NEWZAP", "PR_NEXT_LINK", "PR_NOT_REACHED", "PR_NewCondVar", "PR_NewHashTable", "PR_NewLogModule", "PR_PREV_LINK", "PR_PUBLIC_API", "PR_PUBLIC_DATA", "PR_RANGE_ERROR", "PR_REALLOC", "PR_REMOVE_AND_INIT_LINK", "PR_REMOVE_LINK", "PR_ROUNDUP", "PR_Realloc", "PR_SET_BIT", "PR_STATIC_CALLBACK", "PR_SUCCESS", "PR_SetError", "PR_SetLogBuffering", "PR_SetLogFile", "PR_TEST_BIT", "PR_TRUE", "PR_UINT32", "PR_UPTRDIFF", "prarena_h___", "prbit_h___", "prclist_h___", "prdtoa_h___", "prlog_h___", "prlong_h___", "prmacos_h___", "prmem_h___", "prprf_h___", "prtypes_h___", "prarena", "prbit", "prbitmap_t", "prclist", "prcpucfg", "prdtoa", "prhash", "plhash", "prlong", "prmacos", "prmem", "prosdep", "protypes", "prprf", "prtypes" ); while ($ARGV[0] =~ /^-/) { if ($ARGV[0] eq "-r") { shift; $reverse_conversion = 1; } elsif ($ARGV[0] eq "-outdir") { shift; $outdir = shift; } } # Given an NSPR symbol compute the JS equivalent or # vice-versa sub subst { local ($replacement); local ($sym) = @_; $replacement = substr($sym,0,2) eq "pr" ? "js" : "JS"; $replacement .= substr($sym, 2); return $replacement; } # Build the regular expression that will convert between the NSPR # types and the JS types if ($reverse_conversion) { die "Not implemented yet"; } else { foreach $sym (@NSPR_symbols) { $regexp .= $sym . "|" } # Get rid of the last "!" chop $regexp; # Replace PR* with JS* and replace pr* with js* $regexp = 's/(^|\\W)(' . $regexp . ')/$1 . &subst($2)/eg'; # print $regexp; } # Pre-compile a little subroutine to perform the regexp substitution # between NSPR types and JS types eval('sub convert_from_NSPR {($line) = @_; $line =~ ' . $regexp . ';}'); sub convert_mallocs { ($line) = @_; $line =~ s/PR_MALLOC/malloc/g; $line =~ s/PR_REALLOC/realloc/g; $line =~ s/PR_FREE/free/g; return $line; } sub convert_includes { ($line) = @_; if ($line !~ /include/) { return $line; } if ($line =~ /prlog\.h/) { $line = '#include "jsutil.h"'. " /* Added by JSIFY */\n"; } elsif ($line =~ /plhash\.h/) { $line = '#include "jshash.h"'. " /* Added by JSIFY */\n"; } elsif ($line =~ /plarena\.h/) { $line = '#include "jsarena.h"'. " /* Added by JSIFY */\n"; } elsif ($line =~ /prmem\.h/) { $line = ""; } elsif ($line =~ /jsmsg\.def/) { $line = '#include "js.msg"' . "\n"; } elsif ($line =~ /shellmsg\.def/) { $line = '#include "jsshell.msg"' . "\n"; } elsif ($line =~ /jsopcode\.def/) { $line = '#include "jsopcode.tbl"' . "\n"; } return $line; } sub convert_declarations { ($line) = @_; $line =~ s/PR_EXTERN/JS_EXTERN_API/g; $line =~ s/PR_IMPLEMENT_DATA/JS_EXPORT_DATA/g; $line =~ s/PR_IMPLEMENT/JS_EXPORT_API/g; $line =~ s/PR_CALLBACK/JS_DLL_CALLBACK/g; $line =~ s/PR_STATIC_CALLBACK/JS_STATIC_DLL_CALLBACK/g; $line =~ s/PR_IMPORT/JS_IMPORT/g; $line =~ s/PR_PUBLIC_API/JS_EXPORT_API/g; $line =~ s/PR_PUBLIC_DATA/JS_EXPORT_DATA/g; return $line; } sub convert_long_long_macros { ($line) = @_; $line =~ s/\b(LL_)/JSLL_/g; return $line; } sub convert_asserts { ($line) = @_; $line =~ s/\bPR_ASSERT/JS_ASSERT/g; return $line; } while ($#ARGV >= 0) { $infile = shift; # Change filename, e.g. prtime.h to jsprtime.h, except for legacy # files that start with 'prmj', like prmjtime.h. $outfile = $infile; if ($infile !~ /^prmj/) { $outfile =~ s/^pr/js/; $outfile =~ s/^pl/js/; } if ($outdir) { $outfile = $outdir . '/' . $outfile; } if ($infile eq $outfile) { die "Error: refuse to overwrite $outfile, use -outdir option." } die "Can't open $infile" if !open(INFILE, "<$infile"); die "Can't open $outfile for writing" if !open(OUTFILE, ">$outfile"); while () { $line = $_; #Get rid of #include "prlog.h" &convert_includes($line); # Rename PR_EXTERN, PR_IMPORT, etc. &convert_declarations($line); # Convert from PR_MALLOC to malloc, etc. &convert_mallocs($line); # Convert from PR_ASSERT to JS_ASSERT # &convert_asserts($line); # Convert from, e.g. PRArena to JSPRArena &convert_from_NSPR($line); # Change LL_* macros to JSLL_* &convert_long_long_macros($line); print OUTFILE $line; } } pacparser-1.4.5/src/spidermonkey/js/src/jsinterp.c000066400000000000000000007113561464010763600222320ustar00rootroot00000000000000/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- * vim: set ts=8 sw=4 et tw=78: * * ***** BEGIN LICENSE BLOCK ***** * Version: MPL 1.1/GPL 2.0/LGPL 2.1 * * The contents of this file are subject to the Mozilla Public License Version * 1.1 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * http://www.mozilla.org/MPL/ * * Software distributed under the License is distributed on an "AS IS" basis, * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License * for the specific language governing rights and limitations under the * License. * * The Original Code is Mozilla Communicator client code, released * March 31, 1998. * * The Initial Developer of the Original Code is * Netscape Communications Corporation. * Portions created by the Initial Developer are Copyright (C) 1998 * the Initial Developer. All Rights Reserved. * * Contributor(s): * * Alternatively, the contents of this file may be used under the terms of * either of the GNU General Public License Version 2 or later (the "GPL"), * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), * in which case the provisions of the GPL or the LGPL are applicable instead * of those above. If you wish to allow use of your version of this file only * under the terms of either the GPL or the LGPL, and not to allow others to * use your version of this file under the terms of the MPL, indicate your * decision by deleting the provisions above and replace them with the notice * and other provisions required by the GPL or the LGPL. If you do not delete * the provisions above, a recipient may use your version of this file under * the terms of any one of the MPL, the GPL or the LGPL. * * ***** END LICENSE BLOCK ***** */ /* * JavaScript bytecode interpreter. */ #include "jsstddef.h" #include #include #include #include "jstypes.h" #include "jsarena.h" /* Added by JSIFY */ #include "jsutil.h" /* Added by JSIFY */ #include "jsprf.h" #include "jsapi.h" #include "jsarray.h" #include "jsatom.h" #include "jsbool.h" #include "jscntxt.h" #include "jsconfig.h" #include "jsdbgapi.h" #include "jsfun.h" #include "jsgc.h" #include "jsinterp.h" #include "jsiter.h" #include "jslock.h" #include "jsnum.h" #include "jsobj.h" #include "jsopcode.h" #include "jsscan.h" #include "jsscope.h" #include "jsscript.h" #include "jsstr.h" #if JS_HAS_XML_SUPPORT #include "jsxml.h" #endif #ifdef DEBUG #define ASSERT_CACHE_IS_EMPTY(cache) \ JS_BEGIN_MACRO \ JSPropertyCacheEntry *end_, *pce_, entry_; \ JSPropertyCache *cache_ = (cache); \ JS_ASSERT(cache_->empty); \ end_ = &cache_->table[PROPERTY_CACHE_SIZE]; \ for (pce_ = &cache_->table[0]; pce_ < end_; pce_++) { \ PCE_LOAD(cache_, pce_, entry_); \ JS_ASSERT(!PCE_OBJECT(entry_)); \ JS_ASSERT(!PCE_PROPERTY(entry_)); \ } \ JS_END_MACRO #else #define ASSERT_CACHE_IS_EMPTY(cache) ((void)0) #endif void js_FlushPropertyCache(JSContext *cx) { JSPropertyCache *cache; cache = &cx->runtime->propertyCache; if (cache->empty) { ASSERT_CACHE_IS_EMPTY(cache); return; } memset(cache->table, 0, sizeof cache->table); cache->empty = JS_TRUE; #ifdef JS_PROPERTY_CACHE_METERING cache->flushes++; #endif } void js_DisablePropertyCache(JSContext *cx) { JS_ASSERT(!cx->runtime->propertyCache.disabled); cx->runtime->propertyCache.disabled = JS_TRUE; } void js_EnablePropertyCache(JSContext *cx) { JS_ASSERT(cx->runtime->propertyCache.disabled); ASSERT_CACHE_IS_EMPTY(&cx->runtime->propertyCache); cx->runtime->propertyCache.disabled = JS_FALSE; } /* * Stack macros and functions. These all use a local variable, jsval *sp, to * point to the next free stack slot. SAVE_SP must be called before any call * to a function that may invoke the interpreter. RESTORE_SP must be called * only after return from js_Invoke, because only js_Invoke changes fp->sp. */ #define PUSH(v) (*sp++ = (v)) #define POP() (*--sp) #ifdef DEBUG #define SAVE_SP(fp) \ (JS_ASSERT((fp)->script || !(fp)->spbase || (sp) == (fp)->spbase), \ (fp)->sp = sp) #else #define SAVE_SP(fp) ((fp)->sp = sp) #endif #define RESTORE_SP(fp) (sp = (fp)->sp) /* * SAVE_SP_AND_PC commits deferred stores of interpreter registers to their * homes in fp, when calling out of the interpreter loop or threaded code. * RESTORE_SP_AND_PC copies the other way, to update registers after a call * to a subroutine that interprets a piece of the current script. */ #define SAVE_SP_AND_PC(fp) (SAVE_SP(fp), (fp)->pc = pc) #define RESTORE_SP_AND_PC(fp) (RESTORE_SP(fp), pc = (fp)->pc) /* * Push the generating bytecode's pc onto the parallel pc stack that runs * depth slots below the operands. * * NB: PUSH_OPND uses sp, depth, and pc from its lexical environment. See * js_Interpret for these local variables' declarations and uses. */ #define PUSH_OPND(v) (sp[-depth] = (jsval)pc, PUSH(v)) #define STORE_OPND(n,v) (sp[(n)-depth] = (jsval)pc, sp[n] = (v)) #define POP_OPND() POP() #define FETCH_OPND(n) (sp[n]) /* * Push the jsdouble d using sp, depth, and pc from the lexical environment. * Try to convert d to a jsint that fits in a jsval, otherwise GC-alloc space * for it and push a reference. */ #define STORE_NUMBER(cx, n, d) \ JS_BEGIN_MACRO \ jsint i_; \ jsval v_; \ \ if (JSDOUBLE_IS_INT(d, i_) && INT_FITS_IN_JSVAL(i_)) { \ v_ = INT_TO_JSVAL(i_); \ } else { \ ok = js_NewDoubleValue(cx, d, &v_); \ if (!ok) \ goto out; \ } \ STORE_OPND(n, v_); \ JS_END_MACRO #define STORE_INT(cx, n, i) \ JS_BEGIN_MACRO \ jsval v_; \ \ if (INT_FITS_IN_JSVAL(i)) { \ v_ = INT_TO_JSVAL(i); \ } else { \ ok = js_NewDoubleValue(cx, (jsdouble)(i), &v_); \ if (!ok) \ goto out; \ } \ STORE_OPND(n, v_); \ JS_END_MACRO #define STORE_UINT(cx, n, u) \ JS_BEGIN_MACRO \ jsval v_; \ \ if ((u) <= JSVAL_INT_MAX) { \ v_ = INT_TO_JSVAL(u); \ } else { \ ok = js_NewDoubleValue(cx, (jsdouble)(u), &v_); \ if (!ok) \ goto out; \ } \ STORE_OPND(n, v_); \ JS_END_MACRO #define FETCH_NUMBER(cx, n, d) \ JS_BEGIN_MACRO \ jsval v_; \ \ v_ = FETCH_OPND(n); \ VALUE_TO_NUMBER(cx, v_, d); \ JS_END_MACRO #define FETCH_INT(cx, n, i) \ JS_BEGIN_MACRO \ jsval v_ = FETCH_OPND(n); \ if (JSVAL_IS_INT(v_)) { \ i = JSVAL_TO_INT(v_); \ } else { \ SAVE_SP_AND_PC(fp); \ ok = js_ValueToECMAInt32(cx, v_, &i); \ if (!ok) \ goto out; \ } \ JS_END_MACRO #define FETCH_UINT(cx, n, ui) \ JS_BEGIN_MACRO \ jsval v_ = FETCH_OPND(n); \ jsint i_; \ if (JSVAL_IS_INT(v_) && (i_ = JSVAL_TO_INT(v_)) >= 0) { \ ui = (uint32) i_; \ } else { \ SAVE_SP_AND_PC(fp); \ ok = js_ValueToECMAUint32(cx, v_, &ui); \ if (!ok) \ goto out; \ } \ JS_END_MACRO /* * Optimized conversion macros that test for the desired type in v before * homing sp and calling a conversion function. */ #define VALUE_TO_NUMBER(cx, v, d) \ JS_BEGIN_MACRO \ if (JSVAL_IS_INT(v)) { \ d = (jsdouble)JSVAL_TO_INT(v); \ } else if (JSVAL_IS_DOUBLE(v)) { \ d = *JSVAL_TO_DOUBLE(v); \ } else { \ SAVE_SP_AND_PC(fp); \ ok = js_ValueToNumber(cx, v, &d); \ if (!ok) \ goto out; \ } \ JS_END_MACRO #define POP_BOOLEAN(cx, v, b) \ JS_BEGIN_MACRO \ v = FETCH_OPND(-1); \ if (v == JSVAL_NULL) { \ b = JS_FALSE; \ } else if (JSVAL_IS_BOOLEAN(v)) { \ b = JSVAL_TO_BOOLEAN(v); \ } else { \ SAVE_SP_AND_PC(fp); \ ok = js_ValueToBoolean(cx, v, &b); \ if (!ok) \ goto out; \ } \ sp--; \ JS_END_MACRO /* * Convert a primitive string, number or boolean to a corresponding object. * v must not be an object, null or undefined when using this macro. */ #define PRIMITIVE_TO_OBJECT(cx, v, obj) \ JS_BEGIN_MACRO \ SAVE_SP(fp); \ if (JSVAL_IS_STRING(v)) { \ obj = js_StringToObject(cx, JSVAL_TO_STRING(v)); \ } else if (JSVAL_IS_INT(v)) { \ obj = js_NumberToObject(cx, (jsdouble)JSVAL_TO_INT(v)); \ } else if (JSVAL_IS_DOUBLE(v)) { \ obj = js_NumberToObject(cx, *JSVAL_TO_DOUBLE(v)); \ } else { \ JS_ASSERT(JSVAL_IS_BOOLEAN(v)); \ obj = js_BooleanToObject(cx, JSVAL_TO_BOOLEAN(v)); \ } \ JS_END_MACRO #define VALUE_TO_OBJECT(cx, v, obj) \ JS_BEGIN_MACRO \ if (!JSVAL_IS_PRIMITIVE(v)) { \ obj = JSVAL_TO_OBJECT(v); \ } else { \ SAVE_SP_AND_PC(fp); \ obj = js_ValueToNonNullObject(cx, v); \ if (!obj) { \ ok = JS_FALSE; \ goto out; \ } \ } \ JS_END_MACRO #define FETCH_OBJECT(cx, n, v, obj) \ JS_BEGIN_MACRO \ v = FETCH_OPND(n); \ VALUE_TO_OBJECT(cx, v, obj); \ STORE_OPND(n, OBJECT_TO_JSVAL(obj)); \ JS_END_MACRO #define VALUE_TO_PRIMITIVE(cx, v, hint, vp) \ JS_BEGIN_MACRO \ if (JSVAL_IS_PRIMITIVE(v)) { \ *vp = v; \ } else { \ SAVE_SP_AND_PC(fp); \ ok = OBJ_DEFAULT_VALUE(cx, JSVAL_TO_OBJECT(v), hint, vp); \ if (!ok) \ goto out; \ } \ JS_END_MACRO JS_FRIEND_API(jsval *) js_AllocRawStack(JSContext *cx, uintN nslots, void **markp) { jsval *sp; if (markp) *markp = JS_ARENA_MARK(&cx->stackPool); JS_ARENA_ALLOCATE_CAST(sp, jsval *, &cx->stackPool, nslots * sizeof(jsval)); if (!sp) { JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_STACK_OVERFLOW, (cx->fp && cx->fp->fun) ? JS_GetFunctionName(cx->fp->fun) : "script"); } return sp; } JS_FRIEND_API(void) js_FreeRawStack(JSContext *cx, void *mark) { JS_ARENA_RELEASE(&cx->stackPool, mark); } JS_FRIEND_API(jsval *) js_AllocStack(JSContext *cx, uintN nslots, void **markp) { jsval *sp, *vp, *end; JSArena *a; JSStackHeader *sh; JSStackFrame *fp; /* Callers don't check for zero nslots: we do to avoid empty segments. */ if (nslots == 0) { *markp = NULL; return JS_ARENA_MARK(&cx->stackPool); } /* Allocate 2 extra slots for the stack segment header we'll likely need. */ sp = js_AllocRawStack(cx, 2 + nslots, markp); if (!sp) return NULL; /* Try to avoid another header if we can piggyback on the last segment. */ a = cx->stackPool.current; sh = cx->stackHeaders; if (sh && JS_STACK_SEGMENT(sh) + sh->nslots == sp) { /* Extend the last stack segment, give back the 2 header slots. */ sh->nslots += nslots; a->avail -= 2 * sizeof(jsval); } else { /* * Need a new stack segment, so we must initialize unused slots in the * current frame. See js_GC, just before marking the "operand" jsvals, * where we scan from fp->spbase to fp->sp or through fp->script->depth * (whichever covers fewer slots). */ fp = cx->fp; if (fp && fp->script && fp->spbase) { #ifdef DEBUG jsuword depthdiff = fp->script->depth * sizeof(jsval); JS_ASSERT(JS_UPTRDIFF(fp->sp, fp->spbase) <= depthdiff); JS_ASSERT(JS_UPTRDIFF(*markp, fp->spbase) >= depthdiff); #endif end = fp->spbase + fp->script->depth; for (vp = fp->sp; vp < end; vp++) *vp = JSVAL_VOID; } /* Allocate and push a stack segment header from the 2 extra slots. */ sh = (JSStackHeader *)sp; sh->nslots = nslots; sh->down = cx->stackHeaders; cx->stackHeaders = sh; sp += 2; } /* * Store JSVAL_NULL using memset, to let compilers optimize as they see * fit, in case a caller allocates and pushes GC-things one by one, which * could nest a last-ditch GC that will scan this segment. */ memset(sp, 0, nslots * sizeof(jsval)); return sp; } JS_FRIEND_API(void) js_FreeStack(JSContext *cx, void *mark) { JSStackHeader *sh; jsuword slotdiff; /* Check for zero nslots allocation special case. */ if (!mark) return; /* We can assert because js_FreeStack always balances js_AllocStack. */ sh = cx->stackHeaders; JS_ASSERT(sh); /* If mark is in the current segment, reduce sh->nslots, else pop sh. */ slotdiff = JS_UPTRDIFF(mark, JS_STACK_SEGMENT(sh)) / sizeof(jsval); if (slotdiff < (jsuword)sh->nslots) sh->nslots = slotdiff; else cx->stackHeaders = sh->down; /* Release the stackPool space allocated since mark was set. */ JS_ARENA_RELEASE(&cx->stackPool, mark); } JSBool js_GetArgument(JSContext *cx, JSObject *obj, jsval id, jsval *vp) { return JS_TRUE; } JSBool js_SetArgument(JSContext *cx, JSObject *obj, jsval id, jsval *vp) { return JS_TRUE; } JSBool js_GetLocalVariable(JSContext *cx, JSObject *obj, jsval id, jsval *vp) { return JS_TRUE; } JSBool js_SetLocalVariable(JSContext *cx, JSObject *obj, jsval id, jsval *vp) { return JS_TRUE; } JSObject * js_GetScopeChain(JSContext *cx, JSStackFrame *fp) { JSObject *obj, *cursor, *clonedChild, *parent; JSTempValueRooter tvr; obj = fp->blockChain; if (!obj) { /* * Don't force a call object for a lightweight function call, but do * insist that there is a call object for a heavyweight function call. */ JS_ASSERT(!fp->fun || !(fp->fun->flags & JSFUN_HEAVYWEIGHT) || fp->callobj); JS_ASSERT(fp->scopeChain); return fp->scopeChain; } /* * We have one or more lexical scopes to reflect into fp->scopeChain, so * make sure there's a call object at the current head of the scope chain, * if this frame is a call frame. */ if (fp->fun && !fp->callobj) { JS_ASSERT(OBJ_GET_CLASS(cx, fp->scopeChain) != &js_BlockClass || JS_GetPrivate(cx, fp->scopeChain) != fp); if (!js_GetCallObject(cx, fp, fp->scopeChain)) return NULL; } /* * Clone the block chain. To avoid recursive cloning we set the parent of * the cloned child after we clone the parent. In the following loop when * clonedChild is null it indicates the first iteration when no special GC * rooting is necessary. On the second and the following iterations we * have to protect cloned so far chain against the GC during cloning of * the cursor object. */ cursor = obj; clonedChild = NULL; for (;;) { parent = OBJ_GET_PARENT(cx, cursor); /* * We pass fp->scopeChain and not null even if we override the parent * slot later as null triggers useless calculations of slot's value in * js_NewObject that js_CloneBlockObject calls. */ cursor = js_CloneBlockObject(cx, cursor, fp->scopeChain, fp); if (!cursor) { if (clonedChild) JS_POP_TEMP_ROOT(cx, &tvr); return NULL; } if (!clonedChild) { /* * The first iteration. Check if other follow and root obj if so * to protect the whole cloned chain against GC. */ obj = cursor; if (!parent) break; JS_PUSH_TEMP_ROOT_OBJECT(cx, obj, &tvr); } else { /* * Avoid OBJ_SET_PARENT overhead as clonedChild cannot escape to * other threads. */ clonedChild->slots[JSSLOT_PARENT] = OBJECT_TO_JSVAL(cursor); if (!parent) { JS_ASSERT(tvr.u.value == OBJECT_TO_JSVAL(obj)); JS_POP_TEMP_ROOT(cx, &tvr); break; } } clonedChild = cursor; cursor = parent; } fp->flags |= JSFRAME_POP_BLOCKS; fp->scopeChain = obj; fp->blockChain = NULL; return obj; } /* * Walk the scope chain looking for block scopes whose locals need to be * copied from stack slots into object slots before fp goes away. */ static JSBool PutBlockObjects(JSContext *cx, JSStackFrame *fp) { JSBool ok; JSObject *obj; ok = JS_TRUE; for (obj = fp->scopeChain; obj; obj = OBJ_GET_PARENT(cx, obj)) { if (OBJ_GET_CLASS(cx, obj) == &js_BlockClass) { if (JS_GetPrivate(cx, obj) != fp) break; ok &= js_PutBlockObject(cx, obj); } } return ok; } JSObject * js_ComputeThis(JSContext *cx, JSObject *thisp, jsval *argv) { if (thisp && OBJ_GET_CLASS(cx, thisp) != &js_CallClass) { /* Some objects (e.g., With) delegate 'this' to another object. */ thisp = OBJ_THIS_OBJECT(cx, thisp); if (!thisp) return NULL; } else { /* * ECMA requires "the global object", but in the presence of multiple * top-level objects (windows, frames, or certain layers in the client * object model), we prefer fun's parent. An example that causes this * code to run: * * // in window w1 * function f() { return this } * function g() { return f } * * // in window w2 * var h = w1.g() * alert(h() == w1) * * The alert should display "true". */ if (JSVAL_IS_PRIMITIVE(argv[-2]) || !OBJ_GET_PARENT(cx, JSVAL_TO_OBJECT(argv[-2]))) { thisp = cx->globalObject; } else { jsid id; jsval v; uintN attrs; /* Walk up the parent chain. */ thisp = JSVAL_TO_OBJECT(argv[-2]); id = ATOM_TO_JSID(cx->runtime->atomState.parentAtom); for (;;) { if (!OBJ_CHECK_ACCESS(cx, thisp, id, JSACC_PARENT, &v, &attrs)) return NULL; if (JSVAL_IS_VOID(v)) v = OBJ_GET_SLOT(cx, thisp, JSSLOT_PARENT); if (JSVAL_IS_NULL(v)) break; thisp = JSVAL_TO_OBJECT(v); } } } argv[-1] = OBJECT_TO_JSVAL(thisp); return thisp; } #if JS_HAS_NO_SUCH_METHOD static JSBool NoSuchMethod(JSContext *cx, JSStackFrame *fp, jsval *vp, uint32 flags, uintN argc) { JSObject *thisp, *argsobj; jsval *sp, roots[3]; JSTempValueRooter tvr; jsid id; JSBool ok; jsbytecode *pc; jsatomid atomIndex; /* * We must call js_ComputeThis here to censor Call objects. A performance * hit, since we'll call it again in the normal sequence of invoke events, * but at least it's idempotent. * * Normally, we call ComputeThis after all frame members have been set, * and in particular, after any revision of the callee value at *vp due * to clasp->convert (see below). This matters because ComputeThis may * access *vp via fp->argv[-2], to follow the parent chain to a global * object to use as the 'this' parameter. * * Obviously, here in the JSVAL_IS_PRIMITIVE(v) case, there can't be any * such defaulting of 'this' to callee (v, *vp) ancestor. */ JS_ASSERT(JSVAL_IS_PRIMITIVE(vp[0])); RESTORE_SP(fp); if (JSVAL_IS_OBJECT(vp[1])) { thisp = JSVAL_TO_OBJECT(vp[1]); } else { PRIMITIVE_TO_OBJECT(cx, vp[1], thisp); if (!thisp) return JS_FALSE; vp[1] = OBJECT_TO_JSVAL(thisp); } thisp = js_ComputeThis(cx, thisp, vp + 2); if (!thisp) return JS_FALSE; vp[1] = OBJECT_TO_JSVAL(thisp); /* From here on, control must flow through label out: to return. */ memset(roots, 0, sizeof roots); JS_PUSH_TEMP_ROOT(cx, JS_ARRAY_LENGTH(roots), roots, &tvr); id = ATOM_TO_JSID(cx->runtime->atomState.noSuchMethodAtom); #if JS_HAS_XML_SUPPORT if (OBJECT_IS_XML(cx, thisp)) { JSXMLObjectOps *ops; ops = (JSXMLObjectOps *) thisp->map->ops; thisp = ops->getMethod(cx, thisp, id, &roots[2]); if (!thisp) { ok = JS_FALSE; goto out; } vp[1] = OBJECT_TO_JSVAL(thisp); } else #endif { ok = OBJ_GET_PROPERTY(cx, thisp, id, &roots[2]); if (!ok) goto out; } if (JSVAL_IS_PRIMITIVE(roots[2])) goto not_function; pc = (jsbytecode *) vp[-(intN)fp->script->depth]; switch ((JSOp) *pc) { case JSOP_NAME: case JSOP_GETPROP: #if JS_HAS_XML_SUPPORT case JSOP_GETMETHOD: #endif atomIndex = GET_ATOM_INDEX(pc); roots[0] = ATOM_KEY(js_GetAtom(cx, &fp->script->atomMap, atomIndex)); argsobj = js_NewArrayObject(cx, argc, vp + 2); if (!argsobj) { ok = JS_FALSE; goto out; } roots[1] = OBJECT_TO_JSVAL(argsobj); ok = js_InternalInvoke(cx, thisp, roots[2], flags | JSINVOKE_INTERNAL, 2, roots, &vp[0]); break; default: goto not_function; } out: JS_POP_TEMP_ROOT(cx, &tvr); return ok; not_function: js_ReportIsNotFunction(cx, vp, flags & JSINVOKE_FUNFLAGS); ok = JS_FALSE; goto out; } #endif /* JS_HAS_NO_SUCH_METHOD */ #ifdef DUMP_CALL_TABLE #include "jsclist.h" #include "jshash.h" #include "jsdtoa.h" typedef struct CallKey { jsval callee; /* callee value */ const char *filename; /* function filename or null */ uintN lineno; /* function lineno or 0 */ } CallKey; /* Compensate for typeof null == "object" brain damage. */ #define JSTYPE_NULL JSTYPE_LIMIT #define TYPEOF(cx,v) (JSVAL_IS_NULL(v) ? JSTYPE_NULL : JS_TypeOfValue(cx,v)) #define TYPENAME(t) (((t) == JSTYPE_NULL) ? js_null_str : js_type_str[t]) #define NTYPEHIST (JSTYPE_LIMIT + 1) typedef struct CallValue { uint32 total; /* total call count */ uint32 recycled; /* LRU-recycled calls lost */ uint16 minargc; /* minimum argument count */ uint16 maxargc; /* maximum argument count */ struct ArgInfo { uint32 typeHist[NTYPEHIST]; /* histogram by type */ JSCList lruList; /* top 10 values LRU list */ struct ArgValCount { JSCList lruLink; /* LRU list linkage */ jsval value; /* recently passed value */ uint32 count; /* number of times passed */ char strbuf[112]; /* string conversion buffer */ } topValCounts[10]; /* top 10 value storage */ } argInfo[8]; } CallValue; typedef struct CallEntry { JSHashEntry entry; CallKey key; CallValue value; char name[32]; /* function name copy */ } CallEntry; static void * AllocCallTable(void *pool, size_t size) { return malloc(size); } static void FreeCallTable(void *pool, void *item) { free(item); } static JSHashEntry * AllocCallEntry(void *pool, const void *key) { return (JSHashEntry*) calloc(1, sizeof(CallEntry)); } static void FreeCallEntry(void *pool, JSHashEntry *he, uintN flag) { JS_ASSERT(flag == HT_FREE_ENTRY); free(he); } static JSHashAllocOps callTableAllocOps = { AllocCallTable, FreeCallTable, AllocCallEntry, FreeCallEntry }; JS_STATIC_DLL_CALLBACK(JSHashNumber) js_hash_call_key(const void *key) { CallKey *ck = (CallKey *) key; JSHashNumber hash = (jsuword)ck->callee >> 3; if (ck->filename) { hash = (hash << 4) ^ JS_HashString(ck->filename); hash = (hash << 4) ^ ck->lineno; } return hash; } JS_STATIC_DLL_CALLBACK(intN) js_compare_call_keys(const void *k1, const void *k2) { CallKey *ck1 = (CallKey *)k1, *ck2 = (CallKey *)k2; return ck1->callee == ck2->callee && ((ck1->filename && ck2->filename) ? strcmp(ck1->filename, ck2->filename) == 0 : ck1->filename == ck2->filename) && ck1->lineno == ck2->lineno; } JSHashTable *js_CallTable; size_t js_LogCallToSourceLimit; JS_STATIC_DLL_CALLBACK(intN) CallTableDumper(JSHashEntry *he, intN k, void *arg) { CallEntry *ce = (CallEntry *)he; FILE *fp = (FILE *)arg; uintN argc, i, n; struct ArgInfo *ai; JSType save, type; JSCList *cl; struct ArgValCount *avc; jsval argval; if (ce->key.filename) { /* We're called at the end of the mark phase, so mark our filenames. */ js_MarkScriptFilename(ce->key.filename); fprintf(fp, "%s:%u ", ce->key.filename, ce->key.lineno); } else { fprintf(fp, "@%p ", (void *) ce->key.callee); } if (ce->name[0]) fprintf(fp, "name %s ", ce->name); fprintf(fp, "calls %lu (%lu) argc %u/%u\n", (unsigned long) ce->value.total, (unsigned long) ce->value.recycled, ce->value.minargc, ce->value.maxargc); argc = JS_MIN(ce->value.maxargc, 8); for (i = 0; i < argc; i++) { ai = &ce->value.argInfo[i]; n = 0; save = -1; for (type = JSTYPE_VOID; type <= JSTYPE_LIMIT; type++) { if (ai->typeHist[type]) { save = type; ++n; } } if (n == 1) { fprintf(fp, " arg %u type %s: %lu\n", i, TYPENAME(save), (unsigned long) ai->typeHist[save]); } else { fprintf(fp, " arg %u type histogram:\n", i); for (type = JSTYPE_VOID; type <= JSTYPE_LIMIT; type++) { fprintf(fp, " %9s: %8lu ", TYPENAME(type), (unsigned long) ai->typeHist[type]); for (n = (uintN) JS_HOWMANY(ai->typeHist[type], 10); n > 0; --n) fputc('*', fp); fputc('\n', fp); } } fprintf(fp, " arg %u top 10 values:\n", i); n = 1; for (cl = ai->lruList.prev; cl != &ai->lruList; cl = cl->prev) { avc = (struct ArgValCount *)cl; if (!avc->count) break; argval = avc->value; fprintf(fp, " %9u: %8lu %.*s (%#lx)\n", n, (unsigned long) avc->count, sizeof avc->strbuf, avc->strbuf, argval); ++n; } } return HT_ENUMERATE_NEXT; } void js_DumpCallTable(JSContext *cx) { char name[24]; FILE *fp; static uintN dumpCount; if (!js_CallTable) return; JS_snprintf(name, sizeof name, "/tmp/calltable.dump.%u", dumpCount & 7); dumpCount++; fp = fopen(name, "w"); if (!fp) return; JS_HashTableEnumerateEntries(js_CallTable, CallTableDumper, fp); fclose(fp); } static void LogCall(JSContext *cx, jsval callee, uintN argc, jsval *argv) { CallKey key; const char *name, *cstr; JSFunction *fun; JSHashNumber keyHash; JSHashEntry **hep, *he; CallEntry *ce; uintN i, j; jsval argval; JSType type; struct ArgInfo *ai; struct ArgValCount *avc; JSString *str; if (!js_CallTable) { js_CallTable = JS_NewHashTable(1024, js_hash_call_key, js_compare_call_keys, NULL, &callTableAllocOps, NULL); if (!js_CallTable) return; } key.callee = callee; key.filename = NULL; key.lineno = 0; name = ""; if (VALUE_IS_FUNCTION(cx, callee)) { fun = (JSFunction *) JS_GetPrivate(cx, JSVAL_TO_OBJECT(callee)); if (fun->atom) name = js_AtomToPrintableString(cx, fun->atom); if (FUN_INTERPRETED(fun)) { key.filename = fun->u.i.script->filename; key.lineno = fun->u.i.script->lineno; } } keyHash = js_hash_call_key(&key); hep = JS_HashTableRawLookup(js_CallTable, keyHash, &key); he = *hep; if (he) { ce = (CallEntry *) he; JS_ASSERT(strncmp(ce->name, name, sizeof ce->name) == 0); } else { he = JS_HashTableRawAdd(js_CallTable, hep, keyHash, &key, NULL); if (!he) return; ce = (CallEntry *) he; ce->entry.key = &ce->key; ce->entry.value = &ce->value; ce->key = key; for (i = 0; i < 8; i++) { ai = &ce->value.argInfo[i]; JS_INIT_CLIST(&ai->lruList); for (j = 0; j < 10; j++) JS_APPEND_LINK(&ai->topValCounts[j].lruLink, &ai->lruList); } strncpy(ce->name, name, sizeof ce->name); } ++ce->value.total; if (ce->value.minargc < argc) ce->value.minargc = argc; if (ce->value.maxargc < argc) ce->value.maxargc = argc; if (argc > 8) argc = 8; for (i = 0; i < argc; i++) { ai = &ce->value.argInfo[i]; argval = argv[i]; type = TYPEOF(cx, argval); ++ai->typeHist[type]; for (j = 0; ; j++) { if (j == 10) { avc = (struct ArgValCount *) ai->lruList.next; ce->value.recycled += avc->count; avc->value = argval; avc->count = 1; break; } avc = &ai->topValCounts[j]; if (avc->value == argval) { ++avc->count; break; } } /* Move avc to the back of the LRU list. */ JS_REMOVE_LINK(&avc->lruLink); JS_APPEND_LINK(&avc->lruLink, &ai->lruList); str = NULL; cstr = ""; switch (TYPEOF(cx, argval)) { case JSTYPE_VOID: cstr = js_type_str[JSTYPE_VOID]; break; case JSTYPE_NULL: cstr = js_null_str; break; case JSTYPE_BOOLEAN: cstr = js_boolean_str[JSVAL_TO_BOOLEAN(argval)]; break; case JSTYPE_NUMBER: if (JSVAL_IS_INT(argval)) { JS_snprintf(avc->strbuf, sizeof avc->strbuf, "%ld", JSVAL_TO_INT(argval)); } else { JS_dtostr(avc->strbuf, sizeof avc->strbuf, DTOSTR_STANDARD, 0, *JSVAL_TO_DOUBLE(argval)); } continue; case JSTYPE_STRING: str = js_QuoteString(cx, JSVAL_TO_STRING(argval), (jschar)'"'); break; case JSTYPE_FUNCTION: if (VALUE_IS_FUNCTION(cx, argval)) { fun = (JSFunction *)JS_GetPrivate(cx, JSVAL_TO_OBJECT(argval)); if (fun && fun->atom) { str = ATOM_TO_STRING(fun->atom); break; } } /* FALL THROUGH */ case JSTYPE_OBJECT: js_LogCallToSourceLimit = sizeof avc->strbuf; cx->options |= JSOPTION_LOGCALL_TOSOURCE; str = js_ValueToSource(cx, argval); cx->options &= ~JSOPTION_LOGCALL_TOSOURCE; break; } if (str) cstr = JS_GetStringBytes(str); strncpy(avc->strbuf, cstr, sizeof avc->strbuf); } } #endif /* DUMP_CALL_TABLE */ /* * Conditional assert to detect failure to clear a pending exception that is * suppressed (or unintentional suppression of a wanted exception). */ #if defined DEBUG_brendan || defined DEBUG_mrbkap || defined DEBUG_shaver # define DEBUG_NOT_THROWING 1 #endif #ifdef DEBUG_NOT_THROWING # define ASSERT_NOT_THROWING(cx) JS_ASSERT(!(cx)->throwing) #else # define ASSERT_NOT_THROWING(cx) /* nothing */ #endif /* * Find a function reference and its 'this' object implicit first parameter * under argc arguments on cx's stack, and call the function. Push missing * required arguments, allocate declared local variables, and pop everything * when done. Then push the return value. */ JS_FRIEND_API(JSBool) js_Invoke(JSContext *cx, uintN argc, uintN flags) { void *mark; JSStackFrame *fp, frame; jsval *sp, *newsp, *limit; jsval *vp, v, thisv; JSObject *funobj, *parent, *thisp; JSBool ok; JSClass *clasp; JSObjectOps *ops; JSNative native; JSFunction *fun; JSScript *script; uintN nslots, nvars, nalloc, surplus; JSInterpreterHook hook; void *hookData; /* Mark the top of stack and load frequently-used registers. */ mark = JS_ARENA_MARK(&cx->stackPool); fp = cx->fp; sp = fp->sp; /* * Set vp to the callee value's stack slot (it's where rval goes). * Once vp is set, control should flow through label out2: to return. * Set frame.rval early so native class and object ops can throw and * return false, causing a goto out2 with ok set to false. */ vp = sp - (2 + argc); v = *vp; frame.rval = JSVAL_VOID; /* * A callee must be an object reference, unless its 'this' parameter * implements the __noSuchMethod__ method, in which case that method will * be called like so: * * thisp.__noSuchMethod__(id, args) * * where id is the name of the method that this invocation attempted to * call by name, and args is an Array containing this invocation's actual * parameters. */ if (JSVAL_IS_PRIMITIVE(v)) { #if JS_HAS_NO_SUCH_METHOD if (fp->script && !(flags & JSINVOKE_INTERNAL)) { ok = NoSuchMethod(cx, fp, vp, flags, argc); if (ok) frame.rval = *vp; goto out2; } #endif goto bad; } /* Load thisv after potentially calling NoSuchMethod, which may set it. */ thisv = vp[1]; funobj = JSVAL_TO_OBJECT(v); parent = OBJ_GET_PARENT(cx, funobj); clasp = OBJ_GET_CLASS(cx, funobj); if (clasp != &js_FunctionClass) { /* Function is inlined, all other classes use object ops. */ ops = funobj->map->ops; /* * XXX this makes no sense -- why convert to function if clasp->call? * XXX better to call that hook without converting * XXX the only thing that needs fixing is liveconnect * * Try converting to function, for closure and API compatibility. * We attempt the conversion under all circumstances for 1.2, but * only if there is a call op defined otherwise. */ if ((ops == &js_ObjectOps) ? clasp->call : ops->call) { ok = clasp->convert(cx, funobj, JSTYPE_FUNCTION, &v); if (!ok) goto out2; if (VALUE_IS_FUNCTION(cx, v)) { /* Make vp refer to funobj to keep it available as argv[-2]. */ *vp = v; funobj = JSVAL_TO_OBJECT(v); parent = OBJ_GET_PARENT(cx, funobj); goto have_fun; } } fun = NULL; script = NULL; nslots = nvars = 0; /* Try a call or construct native object op. */ native = (flags & JSINVOKE_CONSTRUCT) ? ops->construct : ops->call; if (!native) goto bad; if (JSVAL_IS_OBJECT(thisv)) { thisp = JSVAL_TO_OBJECT(thisv); } else { PRIMITIVE_TO_OBJECT(cx, thisv, thisp); if (!thisp) goto out2; vp[1] = thisv = OBJECT_TO_JSVAL(thisp); } } else { have_fun: /* Get private data and set derived locals from it. */ fun = (JSFunction *) JS_GetPrivate(cx, funobj); nslots = (fun->nargs > argc) ? fun->nargs - argc : 0; if (FUN_INTERPRETED(fun)) { native = NULL; script = fun->u.i.script; nvars = fun->u.i.nvars; } else { native = fun->u.n.native; script = NULL; nvars = 0; nslots += fun->u.n.extra; } if (JSFUN_BOUND_METHOD_TEST(fun->flags)) { /* Handle bound method special case. */ thisp = parent; } else if (JSVAL_IS_OBJECT(thisv)) { thisp = JSVAL_TO_OBJECT(thisv); } else { uintN thispflags = JSFUN_THISP_FLAGS(fun->flags); JS_ASSERT(!(flags & JSINVOKE_CONSTRUCT)); if (JSVAL_IS_STRING(thisv)) { if (JSFUN_THISP_TEST(thispflags, JSFUN_THISP_STRING)) { thisp = (JSObject *) thisv; goto init_frame; } thisp = js_StringToObject(cx, JSVAL_TO_STRING(thisv)); } else if (JSVAL_IS_INT(thisv)) { if (JSFUN_THISP_TEST(thispflags, JSFUN_THISP_NUMBER)) { thisp = (JSObject *) thisv; goto init_frame; } thisp = js_NumberToObject(cx, (jsdouble)JSVAL_TO_INT(thisv)); } else if (JSVAL_IS_DOUBLE(thisv)) { if (JSFUN_THISP_TEST(thispflags, JSFUN_THISP_NUMBER)) { thisp = (JSObject *) thisv; goto init_frame; } thisp = js_NumberToObject(cx, *JSVAL_TO_DOUBLE(thisv)); } else { JS_ASSERT(JSVAL_IS_BOOLEAN(thisv)); if (JSFUN_THISP_TEST(thispflags, JSFUN_THISP_BOOLEAN)) { thisp = (JSObject *) thisv; goto init_frame; } thisp = js_BooleanToObject(cx, JSVAL_TO_BOOLEAN(thisv)); } if (!thisp) { ok = JS_FALSE; goto out2; } goto init_frame; } } if (flags & JSINVOKE_CONSTRUCT) { /* Default return value for a constructor is the new object. */ frame.rval = OBJECT_TO_JSVAL(thisp); } else { thisp = js_ComputeThis(cx, thisp, vp + 2); if (!thisp) { ok = JS_FALSE; goto out2; } } init_frame: /* Initialize the rest of frame, except for sp (set by SAVE_SP later). */ frame.thisp = thisp; frame.varobj = NULL; frame.callobj = frame.argsobj = NULL; frame.script = script; frame.fun = fun; frame.argc = argc; frame.argv = sp - argc; frame.nvars = nvars; frame.vars = sp; frame.down = fp; frame.annotation = NULL; frame.scopeChain = NULL; /* set below for real, after cx->fp is set */ frame.pc = NULL; frame.spbase = NULL; frame.sharpDepth = 0; frame.sharpArray = NULL; frame.flags = flags; frame.dormantNext = NULL; frame.xmlNamespace = NULL; frame.blockChain = NULL; /* From here on, control must flow through label out: to return. */ cx->fp = &frame; /* Init these now in case we goto out before first hook call. */ hook = cx->runtime->callHook; hookData = NULL; /* Check for argument slots required by the function. */ if (nslots) { /* All arguments must be contiguous, so we may have to copy actuals. */ nalloc = nslots; limit = (jsval *) cx->stackPool.current->limit; JS_ASSERT((jsval *) cx->stackPool.current->base <= sp && sp <= limit); if (sp + nslots > limit) { /* Hit end of arena: we have to copy argv[-2..(argc+nslots-1)]. */ nalloc += 2 + argc; } else { /* Take advantage of surplus slots in the caller's frame depth. */ JS_ASSERT((jsval *)mark >= sp); surplus = (jsval *)mark - sp; nalloc -= surplus; } /* Check whether we have enough space in the caller's frame. */ if ((intN)nalloc > 0) { /* Need space for actuals plus missing formals minus surplus. */ newsp = js_AllocRawStack(cx, nalloc, NULL); if (!newsp) { ok = JS_FALSE; goto out; } /* If we couldn't allocate contiguous args, copy actuals now. */ if (newsp != mark) { JS_ASSERT(sp + nslots > limit); JS_ASSERT(2 + argc + nslots == nalloc); *newsp++ = vp[0]; *newsp++ = vp[1]; if (argc) memcpy(newsp, frame.argv, argc * sizeof(jsval)); frame.argv = newsp; sp = frame.vars = newsp + argc; } } /* Advance frame.vars to make room for the missing args. */ frame.vars += nslots; /* Push void to initialize missing args. */ do { PUSH(JSVAL_VOID); } while (--nslots != 0); } JS_ASSERT(nslots == 0); /* Now allocate stack space for local variables. */ if (nvars) { JS_ASSERT((jsval *)cx->stackPool.current->avail >= frame.vars); surplus = (jsval *)cx->stackPool.current->avail - frame.vars; if (surplus < nvars) { newsp = js_AllocRawStack(cx, nvars, NULL); if (!newsp) { ok = JS_FALSE; goto out; } if (newsp != sp) { /* NB: Discontinuity between argv and vars. */ sp = frame.vars = newsp; } } /* Push void to initialize local variables. */ do { PUSH(JSVAL_VOID); } while (--nvars != 0); } JS_ASSERT(nvars == 0); /* Store the current sp in frame before calling fun. */ SAVE_SP(&frame); /* call the hook if present */ if (hook && (native || script)) hookData = hook(cx, &frame, JS_TRUE, 0, cx->runtime->callHookData); /* Call the function, either a native method or an interpreted script. */ if (native) { #ifdef DEBUG_NOT_THROWING JSBool alreadyThrowing = cx->throwing; #endif #if JS_HAS_LVALUE_RETURN /* Set by JS_SetCallReturnValue2, used to return reference types. */ cx->rval2set = JS_FALSE; #endif /* If native, use caller varobj and scopeChain for eval. */ frame.varobj = fp->varobj; frame.scopeChain = fp->scopeChain; ok = native(cx, frame.thisp, argc, frame.argv, &frame.rval); JS_RUNTIME_METER(cx->runtime, nativeCalls); #ifdef DEBUG_NOT_THROWING if (ok && !alreadyThrowing) ASSERT_NOT_THROWING(cx); #endif } else if (script) { #ifdef DUMP_CALL_TABLE LogCall(cx, *vp, argc, frame.argv); #endif /* Use parent scope so js_GetCallObject can find the right "Call". */ frame.scopeChain = parent; if (JSFUN_HEAVYWEIGHT_TEST(fun->flags)) { /* Scope with a call object parented by the callee's parent. */ if (!js_GetCallObject(cx, &frame, parent)) { ok = JS_FALSE; goto out; } } ok = js_Interpret(cx, script->code, &v); } else { /* fun might be onerror trying to report a syntax error in itself. */ frame.scopeChain = NULL; ok = JS_TRUE; } out: if (hookData) { hook = cx->runtime->callHook; if (hook) hook(cx, &frame, JS_FALSE, &ok, hookData); } /* If frame has a call object, sync values and clear back-pointer. */ if (frame.callobj) ok &= js_PutCallObject(cx, &frame); /* If frame has an arguments object, sync values and clear back-pointer. */ if (frame.argsobj) ok &= js_PutArgsObject(cx, &frame); /* Restore cx->fp now that we're done releasing frame objects. */ cx->fp = fp; out2: /* Pop everything we may have allocated off the stack. */ JS_ARENA_RELEASE(&cx->stackPool, mark); /* Store the return value and restore sp just above it. */ *vp = frame.rval; fp->sp = vp + 1; /* * Store the location of the JSOP_CALL or JSOP_EVAL that generated the * return value, but only if this is an external (compiled from script * source) call that has stack budget for the generating pc. */ if (fp->script && !(flags & JSINVOKE_INTERNAL)) vp[-(intN)fp->script->depth] = (jsval)fp->pc; return ok; bad: js_ReportIsNotFunction(cx, vp, flags & JSINVOKE_FUNFLAGS); ok = JS_FALSE; goto out2; } JSBool js_InternalInvoke(JSContext *cx, JSObject *obj, jsval fval, uintN flags, uintN argc, jsval *argv, jsval *rval) { JSStackFrame *fp, *oldfp, frame; jsval *oldsp, *sp; void *mark; uintN i; JSBool ok; fp = oldfp = cx->fp; if (!fp) { memset(&frame, 0, sizeof frame); cx->fp = fp = &frame; } oldsp = fp->sp; sp = js_AllocStack(cx, 2 + argc, &mark); if (!sp) { ok = JS_FALSE; goto out; } PUSH(fval); PUSH(OBJECT_TO_JSVAL(obj)); for (i = 0; i < argc; i++) PUSH(argv[i]); SAVE_SP(fp); ok = js_Invoke(cx, argc, flags | JSINVOKE_INTERNAL); if (ok) { RESTORE_SP(fp); /* * Store *rval in the a scoped local root if a scope is open, else in * the lastInternalResult pigeon-hole GC root, solely so users of * js_InternalInvoke and its direct and indirect (js_ValueToString for * example) callers do not need to manage roots for local, temporary * references to such results. */ *rval = POP_OPND(); if (JSVAL_IS_GCTHING(*rval)) { if (cx->localRootStack) { if (js_PushLocalRoot(cx, cx->localRootStack, *rval) < 0) ok = JS_FALSE; } else { cx->weakRoots.lastInternalResult = *rval; } } } js_FreeStack(cx, mark); out: fp->sp = oldsp; if (oldfp != fp) cx->fp = oldfp; return ok; } JSBool js_InternalGetOrSet(JSContext *cx, JSObject *obj, jsid id, jsval fval, JSAccessMode mode, uintN argc, jsval *argv, jsval *rval) { int stackDummy; /* * js_InternalInvoke could result in another try to get or set the same id * again, see bug 355497. */ if (!JS_CHECK_STACK_SIZE(cx, stackDummy)) { JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_OVER_RECURSED); return JS_FALSE; } /* * Check general (not object-ops/class-specific) access from the running * script to obj.id only if id has a scripted getter or setter that we're * about to invoke. If we don't check this case, nothing else will -- no * other native code has the chance to check. * * Contrast this non-native (scripted) case with native getter and setter * accesses, where the native itself must do an access check, if security * policies requires it. We make a checkAccess or checkObjectAccess call * back to the embedding program only in those cases where we're not going * to call an embedding-defined native function, getter, setter, or class * hook anyway. Where we do call such a native, there's no need for the * engine to impose a separate access check callback on all embeddings -- * many embeddings have no security policy at all. */ JS_ASSERT(mode == JSACC_READ || mode == JSACC_WRITE); if (cx->runtime->checkObjectAccess && VALUE_IS_FUNCTION(cx, fval) && FUN_INTERPRETED((JSFunction *) JS_GetPrivate(cx, JSVAL_TO_OBJECT(fval))) && !cx->runtime->checkObjectAccess(cx, obj, ID_TO_VALUE(id), mode, &fval)) { return JS_FALSE; } return js_InternalCall(cx, obj, fval, argc, argv, rval); } JSBool js_Execute(JSContext *cx, JSObject *chain, JSScript *script, JSStackFrame *down, uintN flags, jsval *result) { JSInterpreterHook hook; void *hookData, *mark; JSStackFrame *oldfp, frame; JSObject *obj, *tmp; JSBool ok; hook = cx->runtime->executeHook; hookData = mark = NULL; oldfp = cx->fp; frame.script = script; if (down) { /* Propagate arg/var state for eval and the debugger API. */ frame.callobj = down->callobj; frame.argsobj = down->argsobj; frame.varobj = down->varobj; frame.fun = down->fun; frame.thisp = down->thisp; frame.argc = down->argc; frame.argv = down->argv; frame.nvars = down->nvars; frame.vars = down->vars; frame.annotation = down->annotation; frame.sharpArray = down->sharpArray; } else { frame.callobj = frame.argsobj = NULL; obj = chain; if (cx->options & JSOPTION_VAROBJFIX) { while ((tmp = OBJ_GET_PARENT(cx, obj)) != NULL) obj = tmp; } frame.varobj = obj; frame.fun = NULL; frame.thisp = chain; frame.argc = 0; frame.argv = NULL; frame.nvars = script->numGlobalVars; if (frame.nvars) { frame.vars = js_AllocRawStack(cx, frame.nvars, &mark); if (!frame.vars) return JS_FALSE; memset(frame.vars, 0, frame.nvars * sizeof(jsval)); } else { frame.vars = NULL; } frame.annotation = NULL; frame.sharpArray = NULL; } frame.rval = JSVAL_VOID; frame.down = down; frame.scopeChain = chain; frame.pc = NULL; frame.sp = oldfp ? oldfp->sp : NULL; frame.spbase = NULL; frame.sharpDepth = 0; frame.flags = flags; frame.dormantNext = NULL; frame.xmlNamespace = NULL; frame.blockChain = NULL; /* * Here we wrap the call to js_Interpret with code to (conditionally) * save and restore the old stack frame chain into a chain of 'dormant' * frame chains. Since we are replacing cx->fp, we were running into * the problem that if GC was called under this frame, some of the GC * things associated with the old frame chain (available here only in * the C variable 'oldfp') were not rooted and were being collected. * * So, now we preserve the links to these 'dormant' frame chains in cx * before calling js_Interpret and cleanup afterwards. The GC walks * these dormant chains and marks objects in the same way that it marks * objects in the primary cx->fp chain. */ if (oldfp && oldfp != down) { JS_ASSERT(!oldfp->dormantNext); oldfp->dormantNext = cx->dormantFrameChain; cx->dormantFrameChain = oldfp; } cx->fp = &frame; if (hook) hookData = hook(cx, &frame, JS_TRUE, 0, cx->runtime->executeHookData); /* * Use frame.rval, not result, so the last result stays rooted across any * GC activations nested within this js_Interpret. */ ok = js_Interpret(cx, script->code, &frame.rval); *result = frame.rval; if (hookData) { hook = cx->runtime->executeHook; if (hook) hook(cx, &frame, JS_FALSE, &ok, hookData); } if (mark) js_FreeRawStack(cx, mark); cx->fp = oldfp; if (oldfp && oldfp != down) { JS_ASSERT(cx->dormantFrameChain == oldfp); cx->dormantFrameChain = oldfp->dormantNext; oldfp->dormantNext = NULL; } return ok; } #if JS_HAS_EXPORT_IMPORT /* * If id is JSVAL_VOID, import all exported properties from obj. */ static JSBool ImportProperty(JSContext *cx, JSObject *obj, jsid id) { JSBool ok; JSIdArray *ida; JSProperty *prop; JSObject *obj2, *target, *funobj, *closure; JSString *str; uintN attrs; jsint i; jsval value; if (JSVAL_IS_VOID(id)) { ida = JS_Enumerate(cx, obj); if (!ida) return JS_FALSE; ok = JS_TRUE; if (ida->length == 0) goto out; } else { ida = NULL; if (!OBJ_LOOKUP_PROPERTY(cx, obj, id, &obj2, &prop)) return JS_FALSE; if (!prop) { str = js_DecompileValueGenerator(cx, JSDVG_IGNORE_STACK, ID_TO_VALUE(id), NULL); if (str) js_ReportIsNotDefined(cx, JS_GetStringBytes(str)); return JS_FALSE; } ok = OBJ_GET_ATTRIBUTES(cx, obj, id, prop, &attrs); OBJ_DROP_PROPERTY(cx, obj2, prop); if (!ok) return JS_FALSE; if (!(attrs & JSPROP_EXPORTED)) { str = js_DecompileValueGenerator(cx, JSDVG_IGNORE_STACK, ID_TO_VALUE(id), NULL); if (str) { JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_NOT_EXPORTED, JS_GetStringBytes(str)); } return JS_FALSE; } } target = cx->fp->varobj; i = 0; do { if (ida) { id = ida->vector[i]; ok = OBJ_GET_ATTRIBUTES(cx, obj, id, NULL, &attrs); if (!ok) goto out; if (!(attrs & JSPROP_EXPORTED)) continue; } ok = OBJ_CHECK_ACCESS(cx, obj, id, JSACC_IMPORT, &value, &attrs); if (!ok) goto out; if (VALUE_IS_FUNCTION(cx, value)) { funobj = JSVAL_TO_OBJECT(value); closure = js_CloneFunctionObject(cx, funobj, obj); if (!closure) { ok = JS_FALSE; goto out; } value = OBJECT_TO_JSVAL(closure); } /* * Handle the case of importing a property that refers to a local * variable or formal parameter of a function activation. These * properties are accessed by opcodes using stack slot numbers * generated by the compiler rather than runtime name-lookup. These * local references, therefore, bypass the normal scope chain lookup. * So, instead of defining a new property in the activation object, * modify the existing value in the stack slot. */ if (OBJ_GET_CLASS(cx, target) == &js_CallClass) { ok = OBJ_LOOKUP_PROPERTY(cx, target, id, &obj2, &prop); if (!ok) goto out; } else { prop = NULL; } if (prop && target == obj2) { ok = OBJ_SET_PROPERTY(cx, target, id, &value); } else { ok = OBJ_DEFINE_PROPERTY(cx, target, id, value, NULL, NULL, attrs & ~(JSPROP_EXPORTED | JSPROP_GETTER | JSPROP_SETTER), NULL); } if (prop) OBJ_DROP_PROPERTY(cx, obj2, prop); if (!ok) goto out; } while (ida && ++i < ida->length); out: if (ida) JS_DestroyIdArray(cx, ida); return ok; } #endif /* JS_HAS_EXPORT_IMPORT */ JSBool js_CheckRedeclaration(JSContext *cx, JSObject *obj, jsid id, uintN attrs, JSObject **objp, JSProperty **propp) { JSObject *obj2; JSProperty *prop; uintN oldAttrs, report; JSBool isFunction; jsval value; const char *type, *name; if (!OBJ_LOOKUP_PROPERTY(cx, obj, id, &obj2, &prop)) return JS_FALSE; if (propp) { *objp = obj2; *propp = prop; } if (!prop) return JS_TRUE; /* * Use prop as a speedup hint to OBJ_GET_ATTRIBUTES, but drop it on error. * An assertion at label bad: will insist that it is null. */ if (!OBJ_GET_ATTRIBUTES(cx, obj2, id, prop, &oldAttrs)) { OBJ_DROP_PROPERTY(cx, obj2, prop); #ifdef DEBUG prop = NULL; #endif goto bad; } /* * From here, return true, or else goto bad on failure to null out params. * If our caller doesn't want prop, drop it (we don't need it any longer). */ if (!propp) { OBJ_DROP_PROPERTY(cx, obj2, prop); prop = NULL; } /* If either property is readonly, we have an error. */ report = ((oldAttrs | attrs) & JSPROP_READONLY) ? JSREPORT_ERROR : JSREPORT_WARNING | JSREPORT_STRICT; if (report != JSREPORT_ERROR) { /* * Allow redeclaration of variables and functions, but insist that the * new value is not a getter if the old value was, ditto for setters -- * unless prop is impermanent (in which case anyone could delete it and * redefine it, willy-nilly). */ if (!(attrs & (JSPROP_GETTER | JSPROP_SETTER))) return JS_TRUE; if ((~(oldAttrs ^ attrs) & (JSPROP_GETTER | JSPROP_SETTER)) == 0) return JS_TRUE; if (!(oldAttrs & JSPROP_PERMANENT)) return JS_TRUE; report = JSREPORT_ERROR; } isFunction = (oldAttrs & (JSPROP_GETTER | JSPROP_SETTER)) != 0; if (!isFunction) { if (!OBJ_GET_PROPERTY(cx, obj, id, &value)) goto bad; isFunction = VALUE_IS_FUNCTION(cx, value); } type = (oldAttrs & attrs & JSPROP_GETTER) ? js_getter_str : (oldAttrs & attrs & JSPROP_SETTER) ? js_setter_str : (oldAttrs & JSPROP_READONLY) ? js_const_str : isFunction ? js_function_str : js_var_str; name = js_AtomToPrintableString(cx, JSID_TO_ATOM(id)); if (!name) goto bad; return JS_ReportErrorFlagsAndNumber(cx, report, js_GetErrorMessage, NULL, JSMSG_REDECLARED_VAR, type, name); bad: if (propp) { *objp = NULL; *propp = NULL; } JS_ASSERT(!prop); return JS_FALSE; } JSBool js_StrictlyEqual(jsval lval, jsval rval) { jsval ltag = JSVAL_TAG(lval), rtag = JSVAL_TAG(rval); jsdouble ld, rd; if (ltag == rtag) { if (ltag == JSVAL_STRING) { JSString *lstr = JSVAL_TO_STRING(lval), *rstr = JSVAL_TO_STRING(rval); return js_EqualStrings(lstr, rstr); } if (ltag == JSVAL_DOUBLE) { ld = *JSVAL_TO_DOUBLE(lval); rd = *JSVAL_TO_DOUBLE(rval); return JSDOUBLE_COMPARE(ld, ==, rd, JS_FALSE); } return lval == rval; } if (ltag == JSVAL_DOUBLE && JSVAL_IS_INT(rval)) { ld = *JSVAL_TO_DOUBLE(lval); rd = JSVAL_TO_INT(rval); return JSDOUBLE_COMPARE(ld, ==, rd, JS_FALSE); } if (JSVAL_IS_INT(lval) && rtag == JSVAL_DOUBLE) { ld = JSVAL_TO_INT(lval); rd = *JSVAL_TO_DOUBLE(rval); return JSDOUBLE_COMPARE(ld, ==, rd, JS_FALSE); } return lval == rval; } JSBool js_InvokeConstructor(JSContext *cx, jsval *vp, uintN argc) { JSFunction *fun; JSObject *obj, *obj2, *proto, *parent; jsval lval, rval; JSClass *clasp, *funclasp; fun = NULL; obj2 = NULL; lval = *vp; if (!JSVAL_IS_OBJECT(lval) || (obj2 = JSVAL_TO_OBJECT(lval)) == NULL || /* XXX clean up to avoid special cases above ObjectOps layer */ OBJ_GET_CLASS(cx, obj2) == &js_FunctionClass || !obj2->map->ops->construct) { fun = js_ValueToFunction(cx, vp, JSV2F_CONSTRUCT); if (!fun) return JS_FALSE; } clasp = &js_ObjectClass; if (!obj2) { proto = parent = NULL; fun = NULL; } else { /* * Get the constructor prototype object for this function. * Use the nominal 'this' parameter slot, vp[1], as a local * root to protect this prototype, in case it has no other * strong refs. */ if (!OBJ_GET_PROPERTY(cx, obj2, ATOM_TO_JSID(cx->runtime->atomState .classPrototypeAtom), &vp[1])) { return JS_FALSE; } rval = vp[1]; proto = JSVAL_IS_OBJECT(rval) ? JSVAL_TO_OBJECT(rval) : NULL; parent = OBJ_GET_PARENT(cx, obj2); if (OBJ_GET_CLASS(cx, obj2) == &js_FunctionClass) { funclasp = ((JSFunction *)JS_GetPrivate(cx, obj2))->clasp; if (funclasp) clasp = funclasp; } } obj = js_NewObject(cx, clasp, proto, parent); if (!obj) return JS_FALSE; /* Now we have an object with a constructor method; call it. */ vp[1] = OBJECT_TO_JSVAL(obj); if (!js_Invoke(cx, argc, JSINVOKE_CONSTRUCT)) { cx->weakRoots.newborn[GCX_OBJECT] = NULL; return JS_FALSE; } /* Check the return value and if it's primitive, force it to be obj. */ rval = *vp; if (JSVAL_IS_PRIMITIVE(rval)) { if (!fun) { /* native [[Construct]] returning primitive is error */ JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_BAD_NEW_RESULT, js_ValueToPrintableString(cx, rval)); return JS_FALSE; } *vp = OBJECT_TO_JSVAL(obj); } JS_RUNTIME_METER(cx->runtime, constructs); return JS_TRUE; } static JSBool InternStringElementId(JSContext *cx, jsval idval, jsid *idp) { JSAtom *atom; atom = js_ValueToStringAtom(cx, idval); if (!atom) return JS_FALSE; *idp = ATOM_TO_JSID(atom); return JS_TRUE; } static JSBool InternNonIntElementId(JSContext *cx, jsval idval, jsid *idp) { JS_ASSERT(!JSVAL_IS_INT(idval)); #if JS_HAS_XML_SUPPORT if (JSVAL_IS_OBJECT(idval)) { *idp = OBJECT_JSVAL_TO_JSID(idval); return JS_TRUE; } #endif return InternStringElementId(cx, idval, idp); } #if JS_HAS_XML_SUPPORT #define CHECK_ELEMENT_ID(obj, id) \ JS_BEGIN_MACRO \ if (JSID_IS_OBJECT(id) && !OBJECT_IS_XML(cx, obj)) { \ SAVE_SP_AND_PC(fp); \ ok = InternStringElementId(cx, OBJECT_JSID_TO_JSVAL(id), &id); \ if (!ok) \ goto out; \ } \ JS_END_MACRO #else #define CHECK_ELEMENT_ID(obj, id) JS_ASSERT(!JSID_IS_OBJECT(id)) #endif #ifndef MAX_INTERP_LEVEL #if defined(XP_OS2) #define MAX_INTERP_LEVEL 250 #else #define MAX_INTERP_LEVEL 1000 #endif #endif #define MAX_INLINE_CALL_COUNT 1000 /* * Threaded interpretation via computed goto appears to be well-supported by * GCC 3 and higher. IBM's C compiler when run with the right options (e.g., * -qlanglvl=extended) also supports threading. Ditto the SunPro C compiler. * Currently it's broken for JS_VERSION < 160, though this isn't worth fixing. * Add your compiler support macros here. */ #if JS_VERSION >= 160 && ( \ __GNUC__ >= 3 || \ (__IBMC__ >= 700 && defined __IBM_COMPUTED_GOTO) || \ __SUNPRO_C >= 0x570) # define JS_THREADED_INTERP 1 #else # undef JS_THREADED_INTERP #endif JSBool js_Interpret(JSContext *cx, jsbytecode *pc, jsval *result) { JSRuntime *rt; JSStackFrame *fp; JSScript *script; uintN inlineCallCount; JSObject *obj, *obj2, *parent; JSVersion currentVersion, originalVersion; JSBranchCallback onbranch; JSBool ok, cond; JSTrapHandler interruptHandler; jsint depth, len; jsval *sp, *newsp; void *mark; jsbytecode *endpc, *pc2; JSOp op, op2; jsatomid atomIndex; JSAtom *atom; uintN argc, attrs, flags, slot; jsval *vp, lval, rval, ltmp, rtmp; jsid id; JSObject *withobj, *iterobj; JSProperty *prop; JSScopeProperty *sprop; JSString *str, *str2; jsint i, j; jsdouble d, d2; JSClass *clasp; JSFunction *fun; JSType type; #if !defined JS_THREADED_INTERP && defined DEBUG FILE *tracefp = NULL; #endif #if JS_HAS_EXPORT_IMPORT JSIdArray *ida; #endif jsint low, high, off, npairs; JSBool match; #if JS_HAS_GETTER_SETTER JSPropertyOp getter, setter; #endif int stackDummy; #ifdef __GNUC__ # define JS_EXTENSION __extension__ # define JS_EXTENSION_(s) __extension__ ({ s; }) #else # define JS_EXTENSION # define JS_EXTENSION_(s) s #endif #ifdef JS_THREADED_INTERP static void *normalJumpTable[] = { # define OPDEF(op,val,name,token,length,nuses,ndefs,prec,format) \ JS_EXTENSION &&L_##op, # include "jsopcode.tbl" # undef OPDEF }; static void *interruptJumpTable[] = { # define OPDEF(op,val,name,token,length,nuses,ndefs,prec,format) \ ((op != JSOP_PUSHOBJ) \ ? JS_EXTENSION &&interrupt \ : JS_EXTENSION &&L_JSOP_PUSHOBJ), # include "jsopcode.tbl" # undef OPDEF }; register void **jumpTable = normalJumpTable; # define DO_OP() JS_EXTENSION_(goto *jumpTable[op]) # define DO_NEXT_OP(n) do { op = *(pc += (n)); DO_OP(); } while (0) # define BEGIN_CASE(OP) L_##OP: # define END_CASE(OP) DO_NEXT_OP(OP##_LENGTH); # define END_VARLEN_CASE DO_NEXT_OP(len); # define EMPTY_CASE(OP) BEGIN_CASE(OP) op = *++pc; DO_OP(); #else # define DO_OP() goto do_op # define DO_NEXT_OP(n) goto advance_pc # define BEGIN_CASE(OP) case OP: # define END_CASE(OP) break; # define END_VARLEN_CASE break; # define EMPTY_CASE(OP) BEGIN_CASE(OP) END_CASE(OP) #endif *result = JSVAL_VOID; rt = cx->runtime; /* Set registerized frame pointer and derived script pointer. */ fp = cx->fp; script = fp->script; JS_ASSERT(script->length != 0); /* Count of JS function calls that nest in this C js_Interpret frame. */ inlineCallCount = 0; /* * Optimized Get and SetVersion for proper script language versioning. * * If any native method or JSClass/JSObjectOps hook calls js_SetVersion * and changes cx->version, the effect will "stick" and we will stop * maintaining currentVersion. This is relied upon by testsuites, for * the most part -- web browsers select version before compiling and not * at run-time. */ currentVersion = script->version; originalVersion = cx->version; if (currentVersion != originalVersion) js_SetVersion(cx, currentVersion); #ifdef __GNUC__ flags = 0; /* suppress gcc warnings */ id = 0; #endif /* * Prepare to call a user-supplied branch handler, and abort the script * if it returns false. We reload onbranch after calling out to native * functions (but not to getters, setters, or other native hooks). */ #define LOAD_BRANCH_CALLBACK(cx) (onbranch = (cx)->branchCallback) LOAD_BRANCH_CALLBACK(cx); #define CHECK_BRANCH(len) \ JS_BEGIN_MACRO \ if (len <= 0 && onbranch) { \ SAVE_SP_AND_PC(fp); \ if (!(ok = (*onbranch)(cx, script))) \ goto out; \ } \ JS_END_MACRO /* * Load the debugger's interrupt hook here and after calling out to native * functions (but not to getters, setters, or other native hooks), so we do * not have to reload it each time through the interpreter loop -- we hope * the compiler can keep it in a register when it is non-null. */ #ifdef JS_THREADED_INTERP # define LOAD_JUMP_TABLE() \ (jumpTable = interruptHandler ? interruptJumpTable : normalJumpTable) #else # define LOAD_JUMP_TABLE() /* nothing */ #endif #define LOAD_INTERRUPT_HANDLER(rt) \ JS_BEGIN_MACRO \ interruptHandler = (rt)->interruptHandler; \ LOAD_JUMP_TABLE(); \ JS_END_MACRO LOAD_INTERRUPT_HANDLER(rt); /* Check for too much js_Interpret nesting, or too deep a C stack. */ if (++cx->interpLevel == MAX_INTERP_LEVEL || !JS_CHECK_STACK_SIZE(cx, stackDummy)) { JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_OVER_RECURSED); ok = JS_FALSE; goto out2; } /* * Allocate operand and pc stack slots for the script's worst-case depth, * unless we're called to interpret a part of an already active script, a * filtering predicate expression for example. */ depth = (jsint) script->depth; if (JS_LIKELY(!fp->spbase)) { newsp = js_AllocRawStack(cx, (uintN)(2 * depth), &mark); if (!newsp) { ok = JS_FALSE; goto out2; } sp = newsp + depth; fp->spbase = sp; SAVE_SP(fp); } else { sp = fp->sp; JS_ASSERT(JS_UPTRDIFF(sp, fp->spbase) <= depth * sizeof(jsval)); newsp = fp->spbase - depth; mark = NULL; } /* * To support generator_throw and to catch ignored exceptions, fail right * away if cx->throwing is set. If no exception is pending, null obj in * case a callable object is being sent into a yield expression, and the * yield's result is invoked. */ ok = !cx->throwing; if (!ok) { #ifdef DEBUG_NOT_THROWING printf("JS INTERPRETER CALLED WITH PENDING EXCEPTION %lx\n", (unsigned long) cx->exception); #endif goto out; } obj = NULL; #ifdef JS_THREADED_INTERP /* * This is a loop, but it does not look like a loop. The loop-closing * jump is distributed throughout interruptJumpTable, and comes back to * the interrupt label. The dispatch on op is through normalJumpTable. * The trick is LOAD_INTERRUPT_HANDLER setting jumpTable appropriately. * * It is important that "op" be initialized before the interrupt label * because it is possible for "op" to be specially assigned during the * normally processing of an opcode while looping (in particular, this * happens in JSOP_TRAP while debugging). We rely on DO_NEXT_OP to * correctly manage "op" in all other cases. */ op = (JSOp) *pc; if (interruptHandler) { interrupt: SAVE_SP_AND_PC(fp); switch (interruptHandler(cx, script, pc, &rval, rt->interruptHandlerData)) { case JSTRAP_ERROR: ok = JS_FALSE; goto out; case JSTRAP_CONTINUE: break; case JSTRAP_RETURN: fp->rval = rval; goto out; case JSTRAP_THROW: cx->throwing = JS_TRUE; cx->exception = rval; ok = JS_FALSE; goto out; default:; } LOAD_INTERRUPT_HANDLER(rt); } JS_ASSERT((uintN)op < (uintN)JSOP_LIMIT); JS_EXTENSION_(goto *normalJumpTable[op]); #else /* !JS_THREADED_INTERP */ for (;;) { op = (JSOp) *pc; do_op: len = js_CodeSpec[op].length; #ifdef DEBUG tracefp = (FILE *) cx->tracefp; if (tracefp) { intN nuses, n; fprintf(tracefp, "%4u: ", js_PCToLineNumber(cx, script, pc)); js_Disassemble1(cx, script, pc, PTRDIFF(pc, script->code, jsbytecode), JS_FALSE, tracefp); nuses = js_CodeSpec[op].nuses; if (nuses) { SAVE_SP_AND_PC(fp); for (n = -nuses; n < 0; n++) { str = js_DecompileValueGenerator(cx, n, sp[n], NULL); if (str) { fprintf(tracefp, "%s %s", (n == -nuses) ? " inputs:" : ",", JS_GetStringBytes(str)); } } fprintf(tracefp, " @ %d\n", sp - fp->spbase); } } #endif /* DEBUG */ if (interruptHandler && op != JSOP_PUSHOBJ) { SAVE_SP_AND_PC(fp); switch (interruptHandler(cx, script, pc, &rval, rt->interruptHandlerData)) { case JSTRAP_ERROR: ok = JS_FALSE; goto out; case JSTRAP_CONTINUE: break; case JSTRAP_RETURN: fp->rval = rval; goto out; case JSTRAP_THROW: cx->throwing = JS_TRUE; cx->exception = rval; ok = JS_FALSE; goto out; default:; } LOAD_INTERRUPT_HANDLER(rt); } switch (op) { #endif /* !JS_THREADED_INTERP */ BEGIN_CASE(JSOP_STOP) goto out; EMPTY_CASE(JSOP_NOP) BEGIN_CASE(JSOP_GROUP) obj = NULL; END_CASE(JSOP_GROUP) BEGIN_CASE(JSOP_PUSH) PUSH_OPND(JSVAL_VOID); END_CASE(JSOP_PUSH) BEGIN_CASE(JSOP_POP) sp--; END_CASE(JSOP_POP) BEGIN_CASE(JSOP_POP2) sp -= 2; END_CASE(JSOP_POP2) BEGIN_CASE(JSOP_SWAP) vp = sp - depth; /* swap generating pc's for the decompiler */ ltmp = vp[-1]; vp[-1] = vp[-2]; sp[-2] = ltmp; rtmp = sp[-1]; sp[-1] = sp[-2]; sp[-2] = rtmp; END_CASE(JSOP_SWAP) BEGIN_CASE(JSOP_POPV) *result = POP_OPND(); END_CASE(JSOP_POPV) BEGIN_CASE(JSOP_ENTERWITH) FETCH_OBJECT(cx, -1, rval, obj); SAVE_SP_AND_PC(fp); OBJ_TO_INNER_OBJECT(cx, obj); if (!obj || !(obj2 = js_GetScopeChain(cx, fp))) { ok = JS_FALSE; goto out; } withobj = js_NewWithObject(cx, obj, obj2, sp - fp->spbase - 1); if (!withobj) { ok = JS_FALSE; goto out; } fp->scopeChain = withobj; STORE_OPND(-1, OBJECT_TO_JSVAL(withobj)); END_CASE(JSOP_ENTERWITH) BEGIN_CASE(JSOP_LEAVEWITH) rval = POP_OPND(); JS_ASSERT(JSVAL_IS_OBJECT(rval)); withobj = JSVAL_TO_OBJECT(rval); JS_ASSERT(OBJ_GET_CLASS(cx, withobj) == &js_WithClass); fp->scopeChain = OBJ_GET_PARENT(cx, withobj); JS_SetPrivate(cx, withobj, NULL); END_CASE(JSOP_LEAVEWITH) BEGIN_CASE(JSOP_SETRVAL) ASSERT_NOT_THROWING(cx); fp->rval = POP_OPND(); END_CASE(JSOP_SETRVAL) BEGIN_CASE(JSOP_RETURN) CHECK_BRANCH(-1); fp->rval = POP_OPND(); /* FALL THROUGH */ BEGIN_CASE(JSOP_RETRVAL) /* fp->rval already set */ ASSERT_NOT_THROWING(cx); if (inlineCallCount) inline_return: { JSInlineFrame *ifp = (JSInlineFrame *) fp; void *hookData = ifp->hookData; /* * If fp has blocks on its scope chain, home their locals now, * before calling any debugger hook, and before freeing stack. * This matches the order of block putting and hook calling in * the "out-of-line" return code at the bottom of js_Interpret * and in js_Invoke. */ if (fp->flags & JSFRAME_POP_BLOCKS) { SAVE_SP_AND_PC(fp); ok &= PutBlockObjects(cx, fp); } if (hookData) { JSInterpreterHook hook = rt->callHook; if (hook) { SAVE_SP_AND_PC(fp); hook(cx, fp, JS_FALSE, &ok, hookData); LOAD_INTERRUPT_HANDLER(rt); } } /* * If fp has a call object, sync values and clear the back- * pointer. This can happen for a lightweight function if it * calls eval unexpectedly (in a way that is hidden from the * compiler). See bug 325540. */ if (fp->callobj) { SAVE_SP_AND_PC(fp); ok &= js_PutCallObject(cx, fp); } if (fp->argsobj) { SAVE_SP_AND_PC(fp); ok &= js_PutArgsObject(cx, fp); } /* Restore context version only if callee hasn't set version. */ if (JS_LIKELY(cx->version == currentVersion)) { currentVersion = ifp->callerVersion; if (currentVersion != cx->version) js_SetVersion(cx, currentVersion); } /* Store the return value in the caller's operand frame. */ vp = ifp->rvp; *vp = fp->rval; /* Restore cx->fp and release the inline frame's space. */ cx->fp = fp = fp->down; JS_ARENA_RELEASE(&cx->stackPool, ifp->mark); /* Restore sp to point just above the return value. */ fp->sp = vp + 1; RESTORE_SP(fp); /* Restore the calling script's interpreter registers. */ obj = NULL; script = fp->script; depth = (jsint) script->depth; pc = fp->pc; #ifndef JS_THREADED_INTERP endpc = script->code + script->length; #endif /* Store the generating pc for the return value. */ vp[-depth] = (jsval)pc; /* Resume execution in the calling frame. */ inlineCallCount--; if (JS_LIKELY(ok)) { JS_ASSERT(js_CodeSpec[*pc].length == JSOP_CALL_LENGTH); len = JSOP_CALL_LENGTH; DO_NEXT_OP(len); } } goto out; BEGIN_CASE(JSOP_DEFAULT) (void) POP(); /* FALL THROUGH */ BEGIN_CASE(JSOP_GOTO) len = GET_JUMP_OFFSET(pc); CHECK_BRANCH(len); END_VARLEN_CASE BEGIN_CASE(JSOP_IFEQ) POP_BOOLEAN(cx, rval, cond); if (cond == JS_FALSE) { len = GET_JUMP_OFFSET(pc); CHECK_BRANCH(len); DO_NEXT_OP(len); } END_CASE(JSOP_IFEQ) BEGIN_CASE(JSOP_IFNE) POP_BOOLEAN(cx, rval, cond); if (cond != JS_FALSE) { len = GET_JUMP_OFFSET(pc); CHECK_BRANCH(len); DO_NEXT_OP(len); } END_CASE(JSOP_IFNE) BEGIN_CASE(JSOP_OR) POP_BOOLEAN(cx, rval, cond); if (cond == JS_TRUE) { len = GET_JUMP_OFFSET(pc); PUSH_OPND(rval); DO_NEXT_OP(len); } END_CASE(JSOP_OR) BEGIN_CASE(JSOP_AND) POP_BOOLEAN(cx, rval, cond); if (cond == JS_FALSE) { len = GET_JUMP_OFFSET(pc); PUSH_OPND(rval); DO_NEXT_OP(len); } END_CASE(JSOP_AND) BEGIN_CASE(JSOP_DEFAULTX) (void) POP(); /* FALL THROUGH */ BEGIN_CASE(JSOP_GOTOX) len = GET_JUMPX_OFFSET(pc); CHECK_BRANCH(len); END_VARLEN_CASE BEGIN_CASE(JSOP_IFEQX) POP_BOOLEAN(cx, rval, cond); if (cond == JS_FALSE) { len = GET_JUMPX_OFFSET(pc); CHECK_BRANCH(len); DO_NEXT_OP(len); } END_CASE(JSOP_IFEQX) BEGIN_CASE(JSOP_IFNEX) POP_BOOLEAN(cx, rval, cond); if (cond != JS_FALSE) { len = GET_JUMPX_OFFSET(pc); CHECK_BRANCH(len); DO_NEXT_OP(len); } END_CASE(JSOP_IFNEX) BEGIN_CASE(JSOP_ORX) POP_BOOLEAN(cx, rval, cond); if (cond == JS_TRUE) { len = GET_JUMPX_OFFSET(pc); PUSH_OPND(rval); DO_NEXT_OP(len); } END_CASE(JSOP_ORX) BEGIN_CASE(JSOP_ANDX) POP_BOOLEAN(cx, rval, cond); if (cond == JS_FALSE) { len = GET_JUMPX_OFFSET(pc); PUSH_OPND(rval); DO_NEXT_OP(len); } END_CASE(JSOP_ANDX) /* * If the index value at sp[n] is not an int that fits in a jsval, it could * be an object (an XML QName, AttributeName, or AnyName), but only if we are * compiling with JS_HAS_XML_SUPPORT. Otherwise convert the index value to a * string atom id. */ #define FETCH_ELEMENT_ID(n, id) \ JS_BEGIN_MACRO \ jsval idval_ = FETCH_OPND(n); \ if (JSVAL_IS_INT(idval_)) { \ id = INT_JSVAL_TO_JSID(idval_); \ } else { \ SAVE_SP_AND_PC(fp); \ ok = InternNonIntElementId(cx, idval_, &id); \ if (!ok) \ goto out; \ } \ JS_END_MACRO BEGIN_CASE(JSOP_IN) SAVE_SP_AND_PC(fp); rval = FETCH_OPND(-1); if (JSVAL_IS_PRIMITIVE(rval)) { str = js_DecompileValueGenerator(cx, -1, rval, NULL); if (str) { JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_IN_NOT_OBJECT, JS_GetStringBytes(str)); } ok = JS_FALSE; goto out; } obj = JSVAL_TO_OBJECT(rval); FETCH_ELEMENT_ID(-2, id); CHECK_ELEMENT_ID(obj, id); ok = OBJ_LOOKUP_PROPERTY(cx, obj, id, &obj2, &prop); if (!ok) goto out; sp--; STORE_OPND(-1, BOOLEAN_TO_JSVAL(prop != NULL)); if (prop) OBJ_DROP_PROPERTY(cx, obj2, prop); END_CASE(JSOP_IN) BEGIN_CASE(JSOP_FOREACH) flags = JSITER_ENUMERATE | JSITER_FOREACH; goto value_to_iter; #if JS_HAS_DESTRUCTURING BEGIN_CASE(JSOP_FOREACHKEYVAL) flags = JSITER_ENUMERATE | JSITER_FOREACH | JSITER_KEYVALUE; goto value_to_iter; #endif BEGIN_CASE(JSOP_FORIN) /* * Set JSITER_ENUMERATE to indicate that for-in loop should use * the enumeration protocol's iterator for compatibility if an * explicit iterator is not given via the optional __iterator__ * method. */ flags = JSITER_ENUMERATE; value_to_iter: JS_ASSERT(sp > fp->spbase); SAVE_SP_AND_PC(fp); ok = js_ValueToIterator(cx, flags, &sp[-1]); if (!ok) goto out; JS_ASSERT(!JSVAL_IS_PRIMITIVE(sp[-1])); JS_ASSERT(JSOP_FORIN_LENGTH == js_CodeSpec[op].length); END_CASE(JSOP_FORIN) BEGIN_CASE(JSOP_FORPROP) /* * Handle JSOP_FORPROP first, so the cost of the goto do_forinloop * is not paid for the more common cases. */ lval = FETCH_OPND(-1); atom = GET_ATOM(cx, script, pc); id = ATOM_TO_JSID(atom); i = -2; goto do_forinloop; BEGIN_CASE(JSOP_FORNAME) atom = GET_ATOM(cx, script, pc); id = ATOM_TO_JSID(atom); /* * ECMA 12.6.3 says to eval the LHS after looking for properties * to enumerate, and bail without LHS eval if there are no props. * We do Find here to share the most code at label do_forinloop. * If looking for enumerable properties could have side effects, * then we'd have to move this into the common code and condition * it on op == JSOP_FORNAME. */ SAVE_SP_AND_PC(fp); ok = js_FindProperty(cx, id, &obj, &obj2, &prop); if (!ok) goto out; if (prop) OBJ_DROP_PROPERTY(cx, obj2, prop); lval = OBJECT_TO_JSVAL(obj); /* FALL THROUGH */ BEGIN_CASE(JSOP_FORARG) BEGIN_CASE(JSOP_FORVAR) BEGIN_CASE(JSOP_FORLOCAL) /* * JSOP_FORARG and JSOP_FORVAR don't require any lval computation * here, because they address slots on the stack (in fp->args and * fp->vars, respectively). Same applies to JSOP_FORLOCAL, which * addresses fp->spbase. */ /* FALL THROUGH */ BEGIN_CASE(JSOP_FORELEM) /* * JSOP_FORELEM simply initializes or updates the iteration state * and leaves the index expression evaluation and assignment to the * enumerator until after the next property has been acquired, via * a JSOP_ENUMELEM bytecode. */ i = -1; do_forinloop: /* * Reach under the top of stack to find our property iterator, a * JSObject that contains the iteration state. */ JS_ASSERT(!JSVAL_IS_PRIMITIVE(sp[i])); iterobj = JSVAL_TO_OBJECT(sp[i]); SAVE_SP_AND_PC(fp); ok = js_CallIteratorNext(cx, iterobj, &rval); if (!ok) goto out; if (rval == JSVAL_HOLE) { rval = JSVAL_FALSE; goto end_forinloop; } switch (op) { case JSOP_FORARG: slot = GET_ARGNO(pc); JS_ASSERT(slot < fp->fun->nargs); fp->argv[slot] = rval; break; case JSOP_FORVAR: slot = GET_VARNO(pc); JS_ASSERT(slot < fp->fun->u.i.nvars); fp->vars[slot] = rval; break; case JSOP_FORLOCAL: slot = GET_UINT16(pc); JS_ASSERT(slot < (uintN)depth); vp = &fp->spbase[slot]; GC_POKE(cx, *vp); *vp = rval; break; case JSOP_FORELEM: /* FORELEM is not a SET operation, it's more like BINDNAME. */ PUSH_OPND(rval); break; default: JS_ASSERT(op == JSOP_FORPROP || op == JSOP_FORNAME); /* Convert lval to a non-null object containing id. */ VALUE_TO_OBJECT(cx, lval, obj); if (op == JSOP_FORPROP) STORE_OPND(-1, OBJECT_TO_JSVAL(obj)); /* Set the variable obj[id] to refer to rval. */ fp->flags |= JSFRAME_ASSIGNING; ok = OBJ_SET_PROPERTY(cx, obj, id, &rval); fp->flags &= ~JSFRAME_ASSIGNING; if (!ok) goto out; break; } /* Push true to keep looping through properties. */ rval = JSVAL_TRUE; end_forinloop: sp += i + 1; PUSH_OPND(rval); len = js_CodeSpec[op].length; DO_NEXT_OP(len); BEGIN_CASE(JSOP_DUP) JS_ASSERT(sp > fp->spbase); vp = sp - 1; /* address top of stack */ rval = *vp; vp -= depth; /* address generating pc */ vp[1] = *vp; PUSH(rval); END_CASE(JSOP_DUP) BEGIN_CASE(JSOP_DUP2) JS_ASSERT(sp - 2 >= fp->spbase); vp = sp - 1; /* address top of stack */ lval = vp[-1]; rval = *vp; vp -= depth; /* address generating pc */ vp[1] = vp[2] = *vp; PUSH(lval); PUSH(rval); END_CASE(JSOP_DUP2) #define PROPERTY_OP(n, call) \ JS_BEGIN_MACRO \ /* Fetch the left part and resolve it to a non-null object. */ \ FETCH_OBJECT(cx, n, lval, obj); \ \ /* Get or set the property, set ok false if error, true if success. */\ SAVE_SP_AND_PC(fp); \ call; \ if (!ok) \ goto out; \ JS_END_MACRO #define ELEMENT_OP(n, call) \ JS_BEGIN_MACRO \ /* Fetch the right part and resolve it to an internal id. */ \ FETCH_ELEMENT_ID(n, id); \ \ /* Fetch the left part and resolve it to a non-null object. */ \ FETCH_OBJECT(cx, n - 1, lval, obj); \ \ /* Ensure that id has a type suitable for use with obj. */ \ CHECK_ELEMENT_ID(obj, id); \ \ /* Get or set the element, set ok false if error, true if success. */ \ SAVE_SP_AND_PC(fp); \ call; \ if (!ok) \ goto out; \ JS_END_MACRO #define NATIVE_GET(cx,obj,pobj,sprop,vp) \ JS_BEGIN_MACRO \ if (SPROP_HAS_STUB_GETTER(sprop)) { \ /* Fast path for Object instance properties. */ \ JS_ASSERT((sprop)->slot != SPROP_INVALID_SLOT || \ !SPROP_HAS_STUB_SETTER(sprop)); \ *vp = ((sprop)->slot != SPROP_INVALID_SLOT) \ ? LOCKED_OBJ_GET_SLOT(pobj, (sprop)->slot) \ : JSVAL_VOID; \ } else { \ SAVE_SP_AND_PC(fp); \ ok = js_NativeGet(cx, obj, pobj, sprop, vp); \ if (!ok) \ goto out; \ } \ JS_END_MACRO #define NATIVE_SET(cx,obj,sprop,vp) \ JS_BEGIN_MACRO \ if (SPROP_HAS_STUB_SETTER(sprop) && \ (sprop)->slot != SPROP_INVALID_SLOT) { \ /* Fast path for Object instance properties. */ \ LOCKED_OBJ_SET_SLOT(obj, (sprop)->slot, *vp); \ } else { \ SAVE_SP_AND_PC(fp); \ ok = js_NativeSet(cx, obj, sprop, vp); \ if (!ok) \ goto out; \ } \ JS_END_MACRO /* * CACHED_GET and CACHED_SET use cx, obj, id, and rval from their callers' * environments. */ #define CACHED_GET(call) \ JS_BEGIN_MACRO \ if (!OBJ_IS_NATIVE(obj)) { \ ok = call; \ } else { \ JS_LOCK_OBJ(cx, obj); \ PROPERTY_CACHE_TEST(&rt->propertyCache, obj, id, sprop); \ if (sprop) { \ NATIVE_GET(cx, obj, obj, sprop, &rval); \ JS_UNLOCK_OBJ(cx, obj); \ } else { \ JS_UNLOCK_OBJ(cx, obj); \ ok = call; \ /* No fill here: js_GetProperty fills the cache. */ \ } \ } \ JS_END_MACRO #define CACHED_SET(call) \ JS_BEGIN_MACRO \ if (!OBJ_IS_NATIVE(obj)) { \ ok = call; \ } else { \ JSScope *scope_; \ JS_LOCK_OBJ(cx, obj); \ PROPERTY_CACHE_TEST(&rt->propertyCache, obj, id, sprop); \ if (sprop && \ !(sprop->attrs & JSPROP_READONLY) && \ (scope_ = OBJ_SCOPE(obj), !SCOPE_IS_SEALED(scope_))) { \ NATIVE_SET(cx, obj, sprop, &rval); \ JS_UNLOCK_SCOPE(cx, scope_); \ } else { \ JS_UNLOCK_OBJ(cx, obj); \ ok = call; \ /* No fill here: js_SetProperty writes through the cache. */ \ } \ } \ JS_END_MACRO #define BEGIN_LITOPX_CASE(OP,PCOFF) \ BEGIN_CASE(OP) \ pc2 = pc; \ atomIndex = GET_ATOM_INDEX(pc + PCOFF); \ do_##OP: \ atom = js_GetAtom(cx, &script->atomMap, atomIndex); #define END_LITOPX_CASE(OP) \ END_CASE(OP) BEGIN_LITOPX_CASE(JSOP_SETCONST, 0) obj = fp->varobj; rval = FETCH_OPND(-1); SAVE_SP_AND_PC(fp); ok = OBJ_DEFINE_PROPERTY(cx, obj, ATOM_TO_JSID(atom), rval, NULL, NULL, JSPROP_ENUMERATE | JSPROP_PERMANENT | JSPROP_READONLY, NULL); if (!ok) goto out; STORE_OPND(-1, rval); END_LITOPX_CASE(JSOP_SETCONST) #if JS_HAS_DESTRUCTURING BEGIN_CASE(JSOP_ENUMCONSTELEM) FETCH_ELEMENT_ID(-1, id); FETCH_OBJECT(cx, -2, lval, obj); CHECK_ELEMENT_ID(obj, id); rval = FETCH_OPND(-3); SAVE_SP_AND_PC(fp); ok = OBJ_DEFINE_PROPERTY(cx, obj, id, rval, NULL, NULL, JSPROP_ENUMERATE | JSPROP_PERMANENT | JSPROP_READONLY, NULL); if (!ok) goto out; sp -= 3; END_CASE(JSOP_ENUMCONSTELEM) #endif BEGIN_LITOPX_CASE(JSOP_BINDNAME, 0) SAVE_SP_AND_PC(fp); obj = js_FindIdentifierBase(cx, ATOM_TO_JSID(atom)); if (!obj) { ok = JS_FALSE; goto out; } PUSH_OPND(OBJECT_TO_JSVAL(obj)); END_LITOPX_CASE(JSOP_BINDNAME) BEGIN_CASE(JSOP_SETNAME) atom = GET_ATOM(cx, script, pc); id = ATOM_TO_JSID(atom); rval = FETCH_OPND(-1); lval = FETCH_OPND(-2); JS_ASSERT(!JSVAL_IS_PRIMITIVE(lval)); obj = JSVAL_TO_OBJECT(lval); SAVE_SP_AND_PC(fp); CACHED_SET(OBJ_SET_PROPERTY(cx, obj, id, &rval)); if (!ok) goto out; sp--; STORE_OPND(-1, rval); obj = NULL; END_CASE(JSOP_SETNAME) #define INTEGER_OP(OP, EXTRA_CODE) \ JS_BEGIN_MACRO \ FETCH_INT(cx, -1, j); \ FETCH_INT(cx, -2, i); \ EXTRA_CODE \ i = i OP j; \ sp--; \ STORE_INT(cx, -1, i); \ JS_END_MACRO #define BITWISE_OP(OP) INTEGER_OP(OP, (void) 0;) #define SIGNED_SHIFT_OP(OP) INTEGER_OP(OP, j &= 31;) BEGIN_CASE(JSOP_BITOR) BITWISE_OP(|); END_CASE(JSOP_BITOR) BEGIN_CASE(JSOP_BITXOR) BITWISE_OP(^); END_CASE(JSOP_BITXOR) BEGIN_CASE(JSOP_BITAND) BITWISE_OP(&); END_CASE(JSOP_BITAND) #define RELATIONAL_OP(OP) \ JS_BEGIN_MACRO \ rval = FETCH_OPND(-1); \ lval = FETCH_OPND(-2); \ /* Optimize for two int-tagged operands (typical loop control). */ \ if ((lval & rval) & JSVAL_INT) { \ ltmp = lval ^ JSVAL_VOID; \ rtmp = rval ^ JSVAL_VOID; \ if (ltmp && rtmp) { \ cond = JSVAL_TO_INT(lval) OP JSVAL_TO_INT(rval); \ } else { \ d = ltmp ? JSVAL_TO_INT(lval) : *rt->jsNaN; \ d2 = rtmp ? JSVAL_TO_INT(rval) : *rt->jsNaN; \ cond = JSDOUBLE_COMPARE(d, OP, d2, JS_FALSE); \ } \ } else { \ VALUE_TO_PRIMITIVE(cx, lval, JSTYPE_NUMBER, &lval); \ sp[-2] = lval; \ VALUE_TO_PRIMITIVE(cx, rval, JSTYPE_NUMBER, &rval); \ if (JSVAL_IS_STRING(lval) && JSVAL_IS_STRING(rval)) { \ str = JSVAL_TO_STRING(lval); \ str2 = JSVAL_TO_STRING(rval); \ cond = js_CompareStrings(str, str2) OP 0; \ } else { \ VALUE_TO_NUMBER(cx, lval, d); \ VALUE_TO_NUMBER(cx, rval, d2); \ cond = JSDOUBLE_COMPARE(d, OP, d2, JS_FALSE); \ } \ } \ sp--; \ STORE_OPND(-1, BOOLEAN_TO_JSVAL(cond)); \ JS_END_MACRO /* * NB: These macros can't use JS_BEGIN_MACRO/JS_END_MACRO around their bodies * because they begin if/else chains, so callers must not put semicolons after * the call expressions! */ #if JS_HAS_XML_SUPPORT #define XML_EQUALITY_OP(OP) \ if ((ltmp == JSVAL_OBJECT && \ (obj2 = JSVAL_TO_OBJECT(lval)) && \ OBJECT_IS_XML(cx, obj2)) || \ (rtmp == JSVAL_OBJECT && \ (obj2 = JSVAL_TO_OBJECT(rval)) && \ OBJECT_IS_XML(cx, obj2))) { \ JSXMLObjectOps *ops; \ \ ops = (JSXMLObjectOps *) obj2->map->ops; \ if (obj2 == JSVAL_TO_OBJECT(rval)) \ rval = lval; \ SAVE_SP_AND_PC(fp); \ ok = ops->equality(cx, obj2, rval, &cond); \ if (!ok) \ goto out; \ cond = cond OP JS_TRUE; \ } else #define EXTENDED_EQUALITY_OP(OP) \ if (ltmp == JSVAL_OBJECT && \ (obj2 = JSVAL_TO_OBJECT(lval)) && \ ((clasp = OBJ_GET_CLASS(cx, obj2))->flags & JSCLASS_IS_EXTENDED)) { \ JSExtendedClass *xclasp; \ \ xclasp = (JSExtendedClass *) clasp; \ SAVE_SP_AND_PC(fp); \ ok = xclasp->equality(cx, obj2, rval, &cond); \ if (!ok) \ goto out; \ cond = cond OP JS_TRUE; \ } else #else #define XML_EQUALITY_OP(OP) /* nothing */ #define EXTENDED_EQUALITY_OP(OP) /* nothing */ #endif #define EQUALITY_OP(OP, IFNAN) \ JS_BEGIN_MACRO \ rval = FETCH_OPND(-1); \ lval = FETCH_OPND(-2); \ ltmp = JSVAL_TAG(lval); \ rtmp = JSVAL_TAG(rval); \ XML_EQUALITY_OP(OP) \ if (ltmp == rtmp) { \ if (ltmp == JSVAL_STRING) { \ str = JSVAL_TO_STRING(lval); \ str2 = JSVAL_TO_STRING(rval); \ cond = js_EqualStrings(str, str2) OP JS_TRUE; \ } else if (ltmp == JSVAL_DOUBLE) { \ d = *JSVAL_TO_DOUBLE(lval); \ d2 = *JSVAL_TO_DOUBLE(rval); \ cond = JSDOUBLE_COMPARE(d, OP, d2, IFNAN); \ } else { \ EXTENDED_EQUALITY_OP(OP) \ /* Handle all undefined (=>NaN) and int combinations. */ \ cond = lval OP rval; \ } \ } else { \ if (JSVAL_IS_NULL(lval) || JSVAL_IS_VOID(lval)) { \ cond = (JSVAL_IS_NULL(rval) || JSVAL_IS_VOID(rval)) OP 1; \ } else if (JSVAL_IS_NULL(rval) || JSVAL_IS_VOID(rval)) { \ cond = 1 OP 0; \ } else { \ if (ltmp == JSVAL_OBJECT) { \ VALUE_TO_PRIMITIVE(cx, lval, JSTYPE_VOID, &sp[-2]); \ lval = sp[-2]; \ ltmp = JSVAL_TAG(lval); \ } else if (rtmp == JSVAL_OBJECT) { \ VALUE_TO_PRIMITIVE(cx, rval, JSTYPE_VOID, &sp[-1]); \ rval = sp[-1]; \ rtmp = JSVAL_TAG(rval); \ } \ if (ltmp == JSVAL_STRING && rtmp == JSVAL_STRING) { \ str = JSVAL_TO_STRING(lval); \ str2 = JSVAL_TO_STRING(rval); \ cond = js_EqualStrings(str, str2) OP JS_TRUE; \ } else { \ VALUE_TO_NUMBER(cx, lval, d); \ VALUE_TO_NUMBER(cx, rval, d2); \ cond = JSDOUBLE_COMPARE(d, OP, d2, IFNAN); \ } \ } \ } \ sp--; \ STORE_OPND(-1, BOOLEAN_TO_JSVAL(cond)); \ JS_END_MACRO BEGIN_CASE(JSOP_EQ) EQUALITY_OP(==, JS_FALSE); END_CASE(JSOP_EQ) BEGIN_CASE(JSOP_NE) EQUALITY_OP(!=, JS_TRUE); END_CASE(JSOP_NE) #define NEW_EQUALITY_OP(OP) \ JS_BEGIN_MACRO \ rval = FETCH_OPND(-1); \ lval = FETCH_OPND(-2); \ cond = js_StrictlyEqual(lval, rval) OP JS_TRUE; \ sp--; \ STORE_OPND(-1, BOOLEAN_TO_JSVAL(cond)); \ JS_END_MACRO BEGIN_CASE(JSOP_NEW_EQ) NEW_EQUALITY_OP(==); END_CASE(JSOP_NEW_EQ) BEGIN_CASE(JSOP_NEW_NE) NEW_EQUALITY_OP(!=); END_CASE(JSOP_NEW_NE) BEGIN_CASE(JSOP_CASE) pc2 = (jsbytecode *) sp[-2-depth]; NEW_EQUALITY_OP(==); (void) POP(); if (cond) { len = GET_JUMP_OFFSET(pc); CHECK_BRANCH(len); DO_NEXT_OP(len); } sp[-depth] = (jsval)pc2; PUSH(lval); END_CASE(JSOP_CASE) BEGIN_CASE(JSOP_CASEX) pc2 = (jsbytecode *) sp[-2-depth]; NEW_EQUALITY_OP(==); (void) POP(); if (cond) { len = GET_JUMPX_OFFSET(pc); CHECK_BRANCH(len); DO_NEXT_OP(len); } sp[-depth] = (jsval)pc2; PUSH(lval); END_CASE(JSOP_CASEX) BEGIN_CASE(JSOP_LT) RELATIONAL_OP(<); END_CASE(JSOP_LT) BEGIN_CASE(JSOP_LE) RELATIONAL_OP(<=); END_CASE(JSOP_LE) BEGIN_CASE(JSOP_GT) RELATIONAL_OP(>); END_CASE(JSOP_GT) BEGIN_CASE(JSOP_GE) RELATIONAL_OP(>=); END_CASE(JSOP_GE) #undef EQUALITY_OP #undef RELATIONAL_OP BEGIN_CASE(JSOP_LSH) SIGNED_SHIFT_OP(<<); END_CASE(JSOP_LSH) BEGIN_CASE(JSOP_RSH) SIGNED_SHIFT_OP(>>); END_CASE(JSOP_RSH) BEGIN_CASE(JSOP_URSH) { uint32 u; FETCH_INT(cx, -1, j); FETCH_UINT(cx, -2, u); u >>= j & 31; sp--; STORE_UINT(cx, -1, u); } END_CASE(JSOP_URSH) #undef INTEGER_OP #undef BITWISE_OP #undef SIGNED_SHIFT_OP BEGIN_CASE(JSOP_ADD) rval = FETCH_OPND(-1); lval = FETCH_OPND(-2); #if JS_HAS_XML_SUPPORT if (!JSVAL_IS_PRIMITIVE(lval) && (obj2 = JSVAL_TO_OBJECT(lval), OBJECT_IS_XML(cx, obj2)) && VALUE_IS_XML(cx, rval)) { JSXMLObjectOps *ops; ops = (JSXMLObjectOps *) obj2->map->ops; SAVE_SP_AND_PC(fp); ok = ops->concatenate(cx, obj2, rval, &rval); if (!ok) goto out; sp--; STORE_OPND(-1, rval); } else #endif { VALUE_TO_PRIMITIVE(cx, lval, JSTYPE_VOID, &sp[-2]); lval = sp[-2]; VALUE_TO_PRIMITIVE(cx, rval, JSTYPE_VOID, &sp[-1]); rval = sp[-1]; if ((cond = JSVAL_IS_STRING(lval)) || JSVAL_IS_STRING(rval)) { SAVE_SP_AND_PC(fp); if (cond) { str = JSVAL_TO_STRING(lval); ok = (str2 = js_ValueToString(cx, rval)) != NULL; if (!ok) goto out; sp[-1] = STRING_TO_JSVAL(str2); } else { str2 = JSVAL_TO_STRING(rval); ok = (str = js_ValueToString(cx, lval)) != NULL; if (!ok) goto out; sp[-2] = STRING_TO_JSVAL(str); } str = js_ConcatStrings(cx, str, str2); if (!str) { ok = JS_FALSE; goto out; } sp--; STORE_OPND(-1, STRING_TO_JSVAL(str)); } else { VALUE_TO_NUMBER(cx, lval, d); VALUE_TO_NUMBER(cx, rval, d2); d += d2; sp--; STORE_NUMBER(cx, -1, d); } } END_CASE(JSOP_ADD) #define BINARY_OP(OP) \ JS_BEGIN_MACRO \ FETCH_NUMBER(cx, -1, d2); \ FETCH_NUMBER(cx, -2, d); \ d = d OP d2; \ sp--; \ STORE_NUMBER(cx, -1, d); \ JS_END_MACRO BEGIN_CASE(JSOP_SUB) BINARY_OP(-); END_CASE(JSOP_SUB) BEGIN_CASE(JSOP_MUL) BINARY_OP(*); END_CASE(JSOP_MUL) BEGIN_CASE(JSOP_DIV) FETCH_NUMBER(cx, -1, d2); FETCH_NUMBER(cx, -2, d); sp--; if (d2 == 0) { #ifdef XP_WIN /* XXX MSVC miscompiles such that (NaN == 0) */ if (JSDOUBLE_IS_NaN(d2)) rval = DOUBLE_TO_JSVAL(rt->jsNaN); else #endif if (d == 0 || JSDOUBLE_IS_NaN(d)) rval = DOUBLE_TO_JSVAL(rt->jsNaN); else if ((JSDOUBLE_HI32(d) ^ JSDOUBLE_HI32(d2)) >> 31) rval = DOUBLE_TO_JSVAL(rt->jsNegativeInfinity); else rval = DOUBLE_TO_JSVAL(rt->jsPositiveInfinity); STORE_OPND(-1, rval); } else { d /= d2; STORE_NUMBER(cx, -1, d); } END_CASE(JSOP_DIV) BEGIN_CASE(JSOP_MOD) FETCH_NUMBER(cx, -1, d2); FETCH_NUMBER(cx, -2, d); sp--; if (d2 == 0) { STORE_OPND(-1, DOUBLE_TO_JSVAL(rt->jsNaN)); } else { #ifdef XP_WIN /* Workaround MS fmod bug where 42 % (1/0) => NaN, not 42. */ if (!(JSDOUBLE_IS_FINITE(d) && JSDOUBLE_IS_INFINITE(d2))) #endif d = fmod(d, d2); STORE_NUMBER(cx, -1, d); } END_CASE(JSOP_MOD) BEGIN_CASE(JSOP_NOT) POP_BOOLEAN(cx, rval, cond); PUSH_OPND(BOOLEAN_TO_JSVAL(!cond)); END_CASE(JSOP_NOT) BEGIN_CASE(JSOP_BITNOT) FETCH_INT(cx, -1, i); i = ~i; STORE_INT(cx, -1, i); END_CASE(JSOP_BITNOT) BEGIN_CASE(JSOP_NEG) /* * Optimize the case of an int-tagged operand by noting that * INT_FITS_IN_JSVAL(i) => INT_FITS_IN_JSVAL(-i) unless i is 0 * when -i is the negative zero which is jsdouble. */ rval = FETCH_OPND(-1); if (JSVAL_IS_INT(rval) && (i = JSVAL_TO_INT(rval)) != 0) { i = -i; JS_ASSERT(INT_FITS_IN_JSVAL(i)); rval = INT_TO_JSVAL(i); } else { if (JSVAL_IS_DOUBLE(rval)) { d = *JSVAL_TO_DOUBLE(rval); } else { SAVE_SP_AND_PC(fp); ok = js_ValueToNumber(cx, rval, &d); if (!ok) goto out; } #ifdef HPUX /* * Negation of a zero doesn't produce a negative * zero on HPUX. Perform the operation by bit * twiddling. */ JSDOUBLE_HI32(d) ^= JSDOUBLE_HI32_SIGNBIT; #else d = -d; #endif ok = js_NewNumberValue(cx, d, &rval); if (!ok) goto out; } STORE_OPND(-1, rval); END_CASE(JSOP_NEG) BEGIN_CASE(JSOP_POS) rval = FETCH_OPND(-1); if (!JSVAL_IS_NUMBER(rval)) { SAVE_SP_AND_PC(fp); ok = js_ValueToNumber(cx, rval, &d); if (!ok) goto out; ok = js_NewNumberValue(cx, d, &rval); if (!ok) goto out; sp[-1] = rval; } sp[-1-depth] = (jsval)pc; END_CASE(JSOP_POS) BEGIN_CASE(JSOP_NEW) /* Get immediate argc and find the constructor function. */ argc = GET_ARGC(pc); do_new: SAVE_SP_AND_PC(fp); vp = sp - (2 + argc); JS_ASSERT(vp >= fp->spbase); ok = js_InvokeConstructor(cx, vp, argc); if (!ok) goto out; RESTORE_SP(fp); LOAD_BRANCH_CALLBACK(cx); LOAD_INTERRUPT_HANDLER(rt); obj = JSVAL_TO_OBJECT(*vp); len = js_CodeSpec[op].length; DO_NEXT_OP(len); BEGIN_CASE(JSOP_DELNAME) atom = GET_ATOM(cx, script, pc); id = ATOM_TO_JSID(atom); SAVE_SP_AND_PC(fp); ok = js_FindProperty(cx, id, &obj, &obj2, &prop); if (!ok) goto out; /* ECMA says to return true if name is undefined or inherited. */ rval = JSVAL_TRUE; if (prop) { OBJ_DROP_PROPERTY(cx, obj2, prop); ok = OBJ_DELETE_PROPERTY(cx, obj, id, &rval); if (!ok) goto out; } PUSH_OPND(rval); END_CASE(JSOP_DELNAME) BEGIN_CASE(JSOP_DELPROP) atom = GET_ATOM(cx, script, pc); id = ATOM_TO_JSID(atom); PROPERTY_OP(-1, ok = OBJ_DELETE_PROPERTY(cx, obj, id, &rval)); STORE_OPND(-1, rval); END_CASE(JSOP_DELPROP) BEGIN_CASE(JSOP_DELELEM) ELEMENT_OP(-1, ok = OBJ_DELETE_PROPERTY(cx, obj, id, &rval)); sp--; STORE_OPND(-1, rval); END_CASE(JSOP_DELELEM) BEGIN_CASE(JSOP_TYPEOFEXPR) BEGIN_CASE(JSOP_TYPEOF) rval = FETCH_OPND(-1); SAVE_SP_AND_PC(fp); type = JS_TypeOfValue(cx, rval); atom = rt->atomState.typeAtoms[type]; STORE_OPND(-1, ATOM_KEY(atom)); END_CASE(JSOP_TYPEOF) BEGIN_CASE(JSOP_VOID) (void) POP_OPND(); PUSH_OPND(JSVAL_VOID); END_CASE(JSOP_VOID) BEGIN_CASE(JSOP_INCNAME) BEGIN_CASE(JSOP_DECNAME) BEGIN_CASE(JSOP_NAMEINC) BEGIN_CASE(JSOP_NAMEDEC) atom = GET_ATOM(cx, script, pc); id = ATOM_TO_JSID(atom); SAVE_SP_AND_PC(fp); ok = js_FindProperty(cx, id, &obj, &obj2, &prop); if (!ok) goto out; if (!prop) goto atom_not_defined; OBJ_DROP_PROPERTY(cx, obj2, prop); lval = OBJECT_TO_JSVAL(obj); i = 0; goto do_incop; BEGIN_CASE(JSOP_INCPROP) BEGIN_CASE(JSOP_DECPROP) BEGIN_CASE(JSOP_PROPINC) BEGIN_CASE(JSOP_PROPDEC) atom = GET_ATOM(cx, script, pc); id = ATOM_TO_JSID(atom); lval = FETCH_OPND(-1); i = -1; goto do_incop; BEGIN_CASE(JSOP_INCELEM) BEGIN_CASE(JSOP_DECELEM) BEGIN_CASE(JSOP_ELEMINC) BEGIN_CASE(JSOP_ELEMDEC) FETCH_ELEMENT_ID(-1, id); lval = FETCH_OPND(-2); i = -2; do_incop: { const JSCodeSpec *cs; VALUE_TO_OBJECT(cx, lval, obj); if (i < 0) STORE_OPND(i, OBJECT_TO_JSVAL(obj)); CHECK_ELEMENT_ID(obj, id); /* The operand must contain a number. */ SAVE_SP_AND_PC(fp); CACHED_GET(OBJ_GET_PROPERTY(cx, obj, id, &rval)); if (!ok) goto out; /* Preload for use in the if/else immediately below. */ cs = &js_CodeSpec[op]; /* The expression result goes in rtmp, the updated value in rval. */ if (JSVAL_IS_INT(rval) && rval != INT_TO_JSVAL(JSVAL_INT_MIN) && rval != INT_TO_JSVAL(JSVAL_INT_MAX)) { if (cs->format & JOF_POST) { rtmp = rval; (cs->format & JOF_INC) ? (rval += 2) : (rval -= 2); } else { (cs->format & JOF_INC) ? (rval += 2) : (rval -= 2); rtmp = rval; } } else { /* * Initially, rval contains the value to increment or decrement, which is not * yet converted. As above, the expression result goes in rtmp, the updated * value goes in rval. Our caller must set vp to point at a GC-rooted jsval * in which we home rtmp, to protect it from GC in case the unconverted rval * is not a number. */ #define NONINT_INCREMENT_OP_MIDDLE() \ JS_BEGIN_MACRO \ VALUE_TO_NUMBER(cx, rval, d); \ if (cs->format & JOF_POST) { \ rtmp = rval; \ if (!JSVAL_IS_NUMBER(rtmp)) { \ ok = js_NewNumberValue(cx, d, &rtmp); \ if (!ok) \ goto out; \ } \ *vp = rtmp; \ (cs->format & JOF_INC) ? d++ : d--; \ ok = js_NewNumberValue(cx, d, &rval); \ } else { \ (cs->format & JOF_INC) ? ++d : --d; \ ok = js_NewNumberValue(cx, d, &rval); \ rtmp = rval; \ } \ if (!ok) \ goto out; \ JS_END_MACRO if (cs->format & JOF_POST) { /* * We must push early to protect the postfix increment * or decrement result, if converted to a jsdouble from * a non-number value, from GC nesting in the setter. */ vp = sp; PUSH(JSVAL_VOID); SAVE_SP(fp); --i; } #ifdef __GNUC__ else vp = NULL; /* suppress bogus gcc warnings */ #endif NONINT_INCREMENT_OP_MIDDLE(); } fp->flags |= JSFRAME_ASSIGNING; CACHED_SET(OBJ_SET_PROPERTY(cx, obj, id, &rval)); fp->flags &= ~JSFRAME_ASSIGNING; if (!ok) goto out; sp += i; PUSH_OPND(rtmp); len = js_CodeSpec[op].length; DO_NEXT_OP(len); } /* NB: This macro doesn't use JS_BEGIN_MACRO/JS_END_MACRO around its body. */ #define FAST_INCREMENT_OP(SLOT,COUNT,BASE,PRE,OPEQ,MINMAX) \ slot = SLOT; \ JS_ASSERT(slot < fp->fun->COUNT); \ vp = fp->BASE + slot; \ rval = *vp; \ if (!JSVAL_IS_INT(rval) || rval == INT_TO_JSVAL(JSVAL_INT_##MINMAX)) \ goto do_nonint_fast_incop; \ PRE = rval; \ rval OPEQ 2; \ *vp = rval; \ PUSH_OPND(PRE); \ goto end_nonint_fast_incop BEGIN_CASE(JSOP_INCARG) FAST_INCREMENT_OP(GET_ARGNO(pc), nargs, argv, rval, +=, MAX); BEGIN_CASE(JSOP_DECARG) FAST_INCREMENT_OP(GET_ARGNO(pc), nargs, argv, rval, -=, MIN); BEGIN_CASE(JSOP_ARGINC) FAST_INCREMENT_OP(GET_ARGNO(pc), nargs, argv, rtmp, +=, MAX); BEGIN_CASE(JSOP_ARGDEC) FAST_INCREMENT_OP(GET_ARGNO(pc), nargs, argv, rtmp, -=, MIN); BEGIN_CASE(JSOP_INCVAR) FAST_INCREMENT_OP(GET_VARNO(pc), u.i.nvars, vars, rval, +=, MAX); BEGIN_CASE(JSOP_DECVAR) FAST_INCREMENT_OP(GET_VARNO(pc), u.i.nvars, vars, rval, -=, MIN); BEGIN_CASE(JSOP_VARINC) FAST_INCREMENT_OP(GET_VARNO(pc), u.i.nvars, vars, rtmp, +=, MAX); BEGIN_CASE(JSOP_VARDEC) FAST_INCREMENT_OP(GET_VARNO(pc), u.i.nvars, vars, rtmp, -=, MIN); end_nonint_fast_incop: len = JSOP_INCARG_LENGTH; /* all fast incops are same length */ DO_NEXT_OP(len); #undef FAST_INCREMENT_OP do_nonint_fast_incop: { const JSCodeSpec *cs = &js_CodeSpec[op]; NONINT_INCREMENT_OP_MIDDLE(); *vp = rval; PUSH_OPND(rtmp); len = cs->length; DO_NEXT_OP(len); } /* NB: This macro doesn't use JS_BEGIN_MACRO/JS_END_MACRO around its body. */ #define FAST_GLOBAL_INCREMENT_OP(SLOWOP,PRE,OPEQ,MINMAX) \ slot = GET_VARNO(pc); \ JS_ASSERT(slot < fp->nvars); \ lval = fp->vars[slot]; \ if (JSVAL_IS_NULL(lval)) { \ op = SLOWOP; \ DO_OP(); \ } \ slot = JSVAL_TO_INT(lval); \ obj = fp->varobj; \ rval = OBJ_GET_SLOT(cx, obj, slot); \ if (!JSVAL_IS_INT(rval) || rval == INT_TO_JSVAL(JSVAL_INT_##MINMAX)) \ goto do_nonint_fast_global_incop; \ PRE = rval; \ rval OPEQ 2; \ OBJ_SET_SLOT(cx, obj, slot, rval); \ PUSH_OPND(PRE); \ goto end_nonint_fast_global_incop BEGIN_CASE(JSOP_INCGVAR) FAST_GLOBAL_INCREMENT_OP(JSOP_INCNAME, rval, +=, MAX); BEGIN_CASE(JSOP_DECGVAR) FAST_GLOBAL_INCREMENT_OP(JSOP_DECNAME, rval, -=, MIN); BEGIN_CASE(JSOP_GVARINC) FAST_GLOBAL_INCREMENT_OP(JSOP_NAMEINC, rtmp, +=, MAX); BEGIN_CASE(JSOP_GVARDEC) FAST_GLOBAL_INCREMENT_OP(JSOP_NAMEDEC, rtmp, -=, MIN); end_nonint_fast_global_incop: len = JSOP_INCGVAR_LENGTH; /* all gvar incops are same length */ JS_ASSERT(len == js_CodeSpec[op].length); DO_NEXT_OP(len); #undef FAST_GLOBAL_INCREMENT_OP do_nonint_fast_global_incop: { const JSCodeSpec *cs = &js_CodeSpec[op]; vp = sp++; SAVE_SP(fp); NONINT_INCREMENT_OP_MIDDLE(); OBJ_SET_SLOT(cx, obj, slot, rval); STORE_OPND(-1, rtmp); len = cs->length; DO_NEXT_OP(len); } BEGIN_CASE(JSOP_GETPROP) BEGIN_CASE(JSOP_GETXPROP) /* Get an immediate atom naming the property. */ atom = GET_ATOM(cx, script, pc); lval = FETCH_OPND(-1); if (JSVAL_IS_STRING(lval) && atom == cx->runtime->atomState.lengthAtom) { rval = INT_TO_JSVAL(JSSTRING_LENGTH(JSVAL_TO_STRING(lval))); obj = NULL; } else { id = ATOM_TO_JSID(atom); VALUE_TO_OBJECT(cx, lval, obj); STORE_OPND(-1, OBJECT_TO_JSVAL(obj)); SAVE_SP_AND_PC(fp); CACHED_GET(OBJ_GET_PROPERTY(cx, obj, id, &rval)); if (!ok) goto out; } STORE_OPND(-1, rval); END_CASE(JSOP_GETPROP) BEGIN_CASE(JSOP_SETPROP) /* Pop the right-hand side into rval for OBJ_SET_PROPERTY. */ rval = FETCH_OPND(-1); /* Get an immediate atom naming the property. */ atom = GET_ATOM(cx, script, pc); id = ATOM_TO_JSID(atom); PROPERTY_OP(-2, CACHED_SET(OBJ_SET_PROPERTY(cx, obj, id, &rval))); sp--; STORE_OPND(-1, rval); obj = NULL; END_CASE(JSOP_SETPROP) BEGIN_CASE(JSOP_GETELEM) BEGIN_CASE(JSOP_GETXELEM) ELEMENT_OP(-1, CACHED_GET(OBJ_GET_PROPERTY(cx, obj, id, &rval))); sp--; STORE_OPND(-1, rval); END_CASE(JSOP_GETELEM) BEGIN_CASE(JSOP_SETELEM) rval = FETCH_OPND(-1); ELEMENT_OP(-2, CACHED_SET(OBJ_SET_PROPERTY(cx, obj, id, &rval))); sp -= 2; STORE_OPND(-1, rval); obj = NULL; END_CASE(JSOP_SETELEM) BEGIN_CASE(JSOP_ENUMELEM) /* Funky: the value to set is under the [obj, id] pair. */ FETCH_ELEMENT_ID(-1, id); FETCH_OBJECT(cx, -2, lval, obj); CHECK_ELEMENT_ID(obj, id); rval = FETCH_OPND(-3); SAVE_SP_AND_PC(fp); ok = OBJ_SET_PROPERTY(cx, obj, id, &rval); if (!ok) goto out; sp -= 3; END_CASE(JSOP_ENUMELEM) /* * LAZY_ARGS_THISP allows the JSOP_ARGSUB bytecode to defer creation of the * arguments object until it is truly needed. JSOP_ARGSUB optimizes away * arguments objects when the only uses of the 'arguments' parameter are to * fetch individual actual parameters. But if such a use were then invoked, * e.g., arguments[i](), the 'this' parameter would and must bind to the * caller's arguments object. So JSOP_ARGSUB sets obj to LAZY_ARGS_THISP. */ #define LAZY_ARGS_THISP ((JSObject *) JSVAL_VOID) BEGIN_CASE(JSOP_PUSHOBJ) if (obj == LAZY_ARGS_THISP && !(obj = js_GetArgsObject(cx, fp))) { ok = JS_FALSE; goto out; } PUSH_OPND(OBJECT_TO_JSVAL(obj)); END_CASE(JSOP_PUSHOBJ) BEGIN_CASE(JSOP_CALL) BEGIN_CASE(JSOP_EVAL) argc = GET_ARGC(pc); vp = sp - (argc + 2); lval = *vp; SAVE_SP_AND_PC(fp); if (VALUE_IS_FUNCTION(cx, lval) && (obj = JSVAL_TO_OBJECT(lval), fun = (JSFunction *) JS_GetPrivate(cx, obj), FUN_INTERPRETED(fun))) /* inline_call: */ { uintN nframeslots, nvars, nslots, missing; JSArena *a; jsuword avail, nbytes; JSBool overflow; void *newmark; jsval *rvp; JSInlineFrame *newifp; JSInterpreterHook hook; /* Restrict recursion of lightweight functions. */ if (inlineCallCount == MAX_INLINE_CALL_COUNT) { JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_OVER_RECURSED); ok = JS_FALSE; goto out; } /* Compute the total number of stack slots needed for fun. */ nframeslots = JS_HOWMANY(sizeof(JSInlineFrame), sizeof(jsval)); nvars = fun->u.i.nvars; script = fun->u.i.script; depth = (jsint) script->depth; nslots = nframeslots + nvars + 2 * depth; /* Allocate missing expected args adjacent to actual args. */ missing = (fun->nargs > argc) ? fun->nargs - argc : 0; a = cx->stackPool.current; avail = a->avail; newmark = (void *) avail; if (missing) { newsp = sp + missing; overflow = (jsuword) newsp > a->limit; if (overflow) nslots += 2 + argc + missing; else if ((jsuword) newsp > avail) avail = a->avail = (jsuword) newsp; } #ifdef __GNUC__ else overflow = JS_FALSE; /* suppress bogus gcc warnings */ #endif /* Allocate the inline frame with its vars and operand slots. */ newsp = (jsval *) avail; nbytes = nslots * sizeof(jsval); avail += nbytes; if (avail <= a->limit) { a->avail = avail; } else { JS_ARENA_ALLOCATE_CAST(newsp, jsval *, &cx->stackPool, nbytes); if (!newsp) { JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_STACK_OVERFLOW, (fp && fp->fun) ? JS_GetFunctionName(fp->fun) : "script"); goto bad_inline_call; } } /* Move args if missing overflow arena a, push missing args. */ rvp = vp; if (missing) { if (overflow) { memcpy(newsp, vp, (2 + argc) * sizeof(jsval)); vp = newsp; sp = vp + 2 + argc; newsp = sp + missing; } do { PUSH(JSVAL_VOID); } while (--missing != 0); } /* Claim space for the stack frame and initialize it. */ newifp = (JSInlineFrame *) newsp; newsp += nframeslots; newifp->frame.callobj = NULL; newifp->frame.argsobj = NULL; newifp->frame.varobj = NULL; newifp->frame.script = script; newifp->frame.fun = fun; newifp->frame.argc = argc; newifp->frame.argv = vp + 2; newifp->frame.rval = JSVAL_VOID; newifp->frame.nvars = nvars; newifp->frame.vars = newsp; newifp->frame.down = fp; newifp->frame.annotation = NULL; newifp->frame.scopeChain = parent = OBJ_GET_PARENT(cx, obj); newifp->frame.sharpDepth = 0; newifp->frame.sharpArray = NULL; newifp->frame.flags = 0; newifp->frame.dormantNext = NULL; newifp->frame.xmlNamespace = NULL; newifp->frame.blockChain = NULL; newifp->rvp = rvp; newifp->mark = newmark; /* Compute the 'this' parameter now that argv is set. */ if (!JSVAL_IS_OBJECT(vp[1])) { PRIMITIVE_TO_OBJECT(cx, vp[1], obj2); if (!obj2) goto bad_inline_call; vp[1] = OBJECT_TO_JSVAL(obj2); } newifp->frame.thisp = js_ComputeThis(cx, JSFUN_BOUND_METHOD_TEST(fun->flags) ? parent : JSVAL_TO_OBJECT(vp[1]), newifp->frame.argv); if (!newifp->frame.thisp) goto bad_inline_call; #ifdef DUMP_CALL_TABLE LogCall(cx, *vp, argc, vp + 2); #endif /* Push void to initialize local variables. */ sp = newsp; while (nvars--) PUSH(JSVAL_VOID); sp += depth; newifp->frame.spbase = sp; SAVE_SP(&newifp->frame); /* Call the debugger hook if present. */ hook = rt->callHook; if (hook) { newifp->frame.pc = NULL; newifp->hookData = hook(cx, &newifp->frame, JS_TRUE, 0, rt->callHookData); LOAD_INTERRUPT_HANDLER(rt); } else { newifp->hookData = NULL; } /* Scope with a call object parented by the callee's parent. */ if (JSFUN_HEAVYWEIGHT_TEST(fun->flags) && !js_GetCallObject(cx, &newifp->frame, parent)) { goto bad_inline_call; } /* Switch to new version if currentVersion wasn't overridden. */ newifp->callerVersion = cx->version; if (JS_LIKELY(cx->version == currentVersion)) { currentVersion = script->version; if (currentVersion != cx->version) js_SetVersion(cx, currentVersion); } /* Push the frame and set interpreter registers. */ cx->fp = fp = &newifp->frame; pc = script->code; #ifndef JS_THREADED_INTERP endpc = pc + script->length; #endif obj = NULL; inlineCallCount++; JS_RUNTIME_METER(rt, inlineCalls); /* Load first opcode and dispatch it (safe since JSOP_STOP). */ op = *pc; DO_OP(); bad_inline_call: RESTORE_SP(fp); JS_ASSERT(fp->pc == pc); script = fp->script; depth = (jsint) script->depth; js_FreeRawStack(cx, newmark); ok = JS_FALSE; goto out; } ok = js_Invoke(cx, argc, 0); RESTORE_SP(fp); LOAD_BRANCH_CALLBACK(cx); LOAD_INTERRUPT_HANDLER(rt); if (!ok) goto out; JS_RUNTIME_METER(rt, nonInlineCalls); #if JS_HAS_LVALUE_RETURN if (cx->rval2set) { /* * Use the stack depth we didn't claim in our budget, but that * we know is there on account of [fun, this] already having * been pushed, at a minimum (if no args). Those two slots * have been popped and [rval] has been pushed, which leaves * one more slot for rval2 before we might overflow. * * NB: rval2 must be the property identifier, and rval the * object from which to get the property. The pair form an * ECMA "reference type", which can be used on the right- or * left-hand side of assignment ops. Note well: only native * methods can return reference types. See JSOP_SETCALL just * below for the left-hand-side case. */ PUSH_OPND(cx->rval2); ELEMENT_OP(-1, ok = OBJ_GET_PROPERTY(cx, obj, id, &rval)); sp--; STORE_OPND(-1, rval); cx->rval2set = JS_FALSE; } #endif /* JS_HAS_LVALUE_RETURN */ obj = NULL; END_CASE(JSOP_CALL) #if JS_HAS_LVALUE_RETURN BEGIN_CASE(JSOP_SETCALL) argc = GET_ARGC(pc); SAVE_SP_AND_PC(fp); ok = js_Invoke(cx, argc, 0); RESTORE_SP(fp); LOAD_BRANCH_CALLBACK(cx); LOAD_INTERRUPT_HANDLER(rt); if (!ok) goto out; if (!cx->rval2set) { JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_BAD_LEFTSIDE_OF_ASS); ok = JS_FALSE; goto out; } PUSH_OPND(cx->rval2); cx->rval2set = JS_FALSE; obj = NULL; END_CASE(JSOP_SETCALL) #endif BEGIN_CASE(JSOP_NAME) atom = GET_ATOM(cx, script, pc); id = ATOM_TO_JSID(atom); SAVE_SP_AND_PC(fp); ok = js_FindProperty(cx, id, &obj, &obj2, &prop); if (!ok) goto out; if (!prop) { /* Kludge to allow (typeof foo == "undefined") tests. */ len = JSOP_NAME_LENGTH; endpc = script->code + script->length; for (pc2 = pc + len; pc2 < endpc; pc2++) { op2 = (JSOp)*pc2; if (op2 == JSOP_TYPEOF) { PUSH_OPND(JSVAL_VOID); DO_NEXT_OP(len); } if (op2 != JSOP_GROUP) break; } goto atom_not_defined; } /* Take the slow path if prop was not found in a native object. */ if (!OBJ_IS_NATIVE(obj) || !OBJ_IS_NATIVE(obj2)) { OBJ_DROP_PROPERTY(cx, obj2, prop); ok = OBJ_GET_PROPERTY(cx, obj, id, &rval); if (!ok) goto out; } else { sprop = (JSScopeProperty *)prop; NATIVE_GET(cx, obj, obj2, sprop, &rval); OBJ_DROP_PROPERTY(cx, obj2, prop); } PUSH_OPND(rval); END_CASE(JSOP_NAME) BEGIN_CASE(JSOP_UINT16) i = (jsint) GET_ATOM_INDEX(pc); rval = INT_TO_JSVAL(i); PUSH_OPND(rval); obj = NULL; END_CASE(JSOP_UINT16) BEGIN_CASE(JSOP_UINT24) i = (jsint) GET_LITERAL_INDEX(pc); rval = INT_TO_JSVAL(i); PUSH_OPND(rval); END_CASE(JSOP_UINT24) BEGIN_CASE(JSOP_LITERAL) atomIndex = GET_LITERAL_INDEX(pc); atom = js_GetAtom(cx, &script->atomMap, atomIndex); PUSH_OPND(ATOM_KEY(atom)); obj = NULL; END_CASE(JSOP_LITERAL) BEGIN_CASE(JSOP_FINDNAME) atomIndex = GET_LITERAL_INDEX(pc); atom = js_GetAtom(cx, &script->atomMap, atomIndex); SAVE_SP_AND_PC(fp); obj = js_FindIdentifierBase(cx, ATOM_TO_JSID(atom)); if (!obj) { ok = JS_FALSE; goto out; } PUSH_OPND(OBJECT_TO_JSVAL(obj)); PUSH_OPND(ATOM_KEY(atom)); END_CASE(JSOP_FINDNAME) BEGIN_CASE(JSOP_LITOPX) /* * Load atomIndex, which is used by code at each do_JSOP_* label. * * Also set pc2 to point at the bytecode extended by this prefix * to have a leading 24 bit atomIndex, instead of the unextended * 16-bit atomIndex that normally comes after op. This enables * JOF_INDEXCONST format ops (which have multiple immediates) to * collect their other immediate via GET_VARNO(pc2) or similar. * * Finally, load op and, if threading, adjust pc so that it will * be advanced properly at the end of op's case by DO_NEXT_OP. */ atomIndex = GET_LITERAL_INDEX(pc); pc2 = pc + 1 + LITERAL_INDEX_LEN; op = *pc2; pc += JSOP_LITOPX_LENGTH - (1 + ATOM_INDEX_LEN); #ifndef JS_THREADED_INTERP len = js_CodeSpec[op].length; #endif switch (op) { case JSOP_ANONFUNOBJ: goto do_JSOP_ANONFUNOBJ; case JSOP_BINDNAME: goto do_JSOP_BINDNAME; case JSOP_CLOSURE: goto do_JSOP_CLOSURE; case JSOP_DEFCONST: goto do_JSOP_DEFCONST; case JSOP_DEFFUN: goto do_JSOP_DEFFUN; case JSOP_DEFLOCALFUN: goto do_JSOP_DEFLOCALFUN; case JSOP_DEFVAR: goto do_JSOP_DEFVAR; #if JS_HAS_EXPORT_IMPORT case JSOP_EXPORTNAME: goto do_JSOP_EXPORTNAME; #endif #if JS_HAS_XML_SUPPORT case JSOP_GETMETHOD: goto do_JSOP_GETMETHOD; case JSOP_SETMETHOD: goto do_JSOP_SETMETHOD; #endif case JSOP_NAMEDFUNOBJ: goto do_JSOP_NAMEDFUNOBJ; case JSOP_NUMBER: goto do_JSOP_NUMBER; case JSOP_OBJECT: goto do_JSOP_OBJECT; #if JS_HAS_XML_SUPPORT case JSOP_QNAMECONST: goto do_JSOP_QNAMECONST; case JSOP_QNAMEPART: goto do_JSOP_QNAMEPART; #endif case JSOP_REGEXP: goto do_JSOP_REGEXP; case JSOP_SETCONST: goto do_JSOP_SETCONST; case JSOP_STRING: goto do_JSOP_STRING; #if JS_HAS_XML_SUPPORT case JSOP_XMLCDATA: goto do_JSOP_XMLCDATA; case JSOP_XMLCOMMENT: goto do_JSOP_XMLCOMMENT; case JSOP_XMLOBJECT: goto do_JSOP_XMLOBJECT; case JSOP_XMLPI: goto do_JSOP_XMLPI; #endif case JSOP_ENTERBLOCK: goto do_JSOP_ENTERBLOCK; default: JS_ASSERT(0); } /* NOTREACHED */ BEGIN_CASE(JSOP_NUMBER) BEGIN_CASE(JSOP_STRING) BEGIN_CASE(JSOP_OBJECT) atomIndex = GET_ATOM_INDEX(pc); do_JSOP_NUMBER: do_JSOP_STRING: do_JSOP_OBJECT: atom = js_GetAtom(cx, &script->atomMap, atomIndex); PUSH_OPND(ATOM_KEY(atom)); obj = NULL; END_CASE(JSOP_NUMBER) BEGIN_LITOPX_CASE(JSOP_REGEXP, 0) { JSRegExp *re; JSObject *funobj; /* * Push a regexp object for the atom mapped by the bytecode at pc, * cloning the literal's regexp object if necessary, to simulate in * the pre-compile/execute-later case what ECMA specifies for the * compile-and-go case: that scanning each regexp literal creates * a single corresponding RegExp object. * * To support pre-compilation transparently, we must handle the * case where a regexp object literal is used in a different global * at execution time from the global with which it was scanned at * compile time. We do this by re-wrapping the JSRegExp private * data struct with a cloned object having the right prototype and * parent, and having its own lastIndex property value storage. * * Unlike JSOP_DEFFUN and other prolog bytecodes that may clone * literal objects, we don't want to pay a script prolog execution * price for all regexp literals in a script (many may not be used * by a particular execution of that script, depending on control * flow), so we initialize lazily here. * * XXX This code is specific to regular expression objects. If we * need a similar op for other kinds of object literals, we should * push cloning down under JSObjectOps and reuse code here. */ JS_ASSERT(ATOM_IS_OBJECT(atom)); obj = ATOM_TO_OBJECT(atom); JS_ASSERT(OBJ_GET_CLASS(cx, obj) == &js_RegExpClass); re = (JSRegExp *) JS_GetPrivate(cx, obj); slot = re->cloneIndex; if (fp->fun) { /* * We're in function code, not global or eval code (in eval * code, JSOP_REGEXP is never emitted). The code generator * recorded in fp->fun->nregexps the number of re->cloneIndex * slots that it reserved in the cloned funobj. */ funobj = JSVAL_TO_OBJECT(fp->argv[-2]); slot += JSCLASS_RESERVED_SLOTS(&js_FunctionClass); if (!JS_GetReservedSlot(cx, funobj, slot, &rval)) return JS_FALSE; if (JSVAL_IS_VOID(rval)) rval = JSVAL_NULL; } else { /* * We're in global code. The code generator already arranged * via script->numGlobalVars to reserve a global variable slot * at cloneIndex. All global variable slots are initialized * to null, not void, for faster testing in JSOP_*GVAR cases. */ rval = fp->vars[slot]; #ifdef __GNUC__ funobj = NULL; /* suppress bogus gcc warnings */ #endif } if (JSVAL_IS_NULL(rval)) { /* Compute the current global object in obj2. */ obj2 = fp->scopeChain; while ((parent = OBJ_GET_PARENT(cx, obj2)) != NULL) obj2 = parent; /* * We must home sp here, because either js_CloneRegExpObject * or JS_SetReservedSlot could nest a last-ditch GC. We home * pc as well, in case js_CloneRegExpObject has to lookup the * "RegExp" class in the global object, which could entail a * JSNewResolveOp call. */ SAVE_SP_AND_PC(fp); /* * If obj's parent is not obj2, we must clone obj so that it * has the right parent, and therefore, the right prototype. * * Yes, this means we assume that the correct RegExp.prototype * to which regexp instances (including literals) delegate can * be distinguished solely by the instance's parent, which was * set to the parent of the RegExp constructor function object * when the instance was created. In other words, * * (/x/.__parent__ == RegExp.__parent__) implies * (/x/.__proto__ == RegExp.prototype) * * (unless you assign a different object to RegExp.prototype * at runtime, in which case, ECMA doesn't specify operation, * and you get what you deserve). * * This same coupling between instance parent and constructor * parent turns up everywhere (see jsobj.c's FindClassObject, * js_ConstructObject, and js_NewObject). It's fundamental to * the design of the language when you consider multiple global * objects and separate compilation and execution, even though * it is not specified fully in ECMA. */ if (OBJ_GET_PARENT(cx, obj) != obj2) { obj = js_CloneRegExpObject(cx, obj, obj2); if (!obj) { ok = JS_FALSE; goto out; } } rval = OBJECT_TO_JSVAL(obj); /* Store the regexp object value in its cloneIndex slot. */ if (fp->fun) { if (!JS_SetReservedSlot(cx, funobj, slot, rval)) return JS_FALSE; } else { fp->vars[slot] = rval; } } PUSH_OPND(rval); obj = NULL; } END_LITOPX_CASE(JSOP_REGEXP) BEGIN_CASE(JSOP_ZERO) PUSH_OPND(JSVAL_ZERO); obj = NULL; END_CASE(JSOP_ZERO) BEGIN_CASE(JSOP_ONE) PUSH_OPND(JSVAL_ONE); obj = NULL; END_CASE(JSOP_ONE) BEGIN_CASE(JSOP_NULL) PUSH_OPND(JSVAL_NULL); obj = NULL; END_CASE(JSOP_NULL) BEGIN_CASE(JSOP_THIS) obj = fp->thisp; clasp = OBJ_GET_CLASS(cx, obj); if (clasp->flags & JSCLASS_IS_EXTENDED) { JSExtendedClass *xclasp; xclasp = (JSExtendedClass *) clasp; if (xclasp->outerObject) { obj = xclasp->outerObject(cx, obj); if (!obj) { ok = JS_FALSE; goto out; } } } PUSH_OPND(OBJECT_TO_JSVAL(obj)); obj = NULL; END_CASE(JSOP_THIS) BEGIN_CASE(JSOP_FALSE) PUSH_OPND(JSVAL_FALSE); obj = NULL; END_CASE(JSOP_FALSE) BEGIN_CASE(JSOP_TRUE) PUSH_OPND(JSVAL_TRUE); obj = NULL; END_CASE(JSOP_TRUE) BEGIN_CASE(JSOP_TABLESWITCH) pc2 = pc; len = GET_JUMP_OFFSET(pc2); /* * ECMAv2+ forbids conversion of discriminant, so we will skip to * the default case if the discriminant isn't already an int jsval. * (This opcode is emitted only for dense jsint-domain switches.) */ rval = POP_OPND(); if (!JSVAL_IS_INT(rval)) DO_NEXT_OP(len); i = JSVAL_TO_INT(rval); pc2 += JUMP_OFFSET_LEN; low = GET_JUMP_OFFSET(pc2); pc2 += JUMP_OFFSET_LEN; high = GET_JUMP_OFFSET(pc2); i -= low; if ((jsuint)i < (jsuint)(high - low + 1)) { pc2 += JUMP_OFFSET_LEN + JUMP_OFFSET_LEN * i; off = (jsint) GET_JUMP_OFFSET(pc2); if (off) len = off; } END_VARLEN_CASE BEGIN_CASE(JSOP_LOOKUPSWITCH) lval = POP_OPND(); pc2 = pc; len = GET_JUMP_OFFSET(pc2); if (!JSVAL_IS_NUMBER(lval) && !JSVAL_IS_STRING(lval) && !JSVAL_IS_BOOLEAN(lval)) { DO_NEXT_OP(len); } pc2 += JUMP_OFFSET_LEN; npairs = (jsint) GET_ATOM_INDEX(pc2); pc2 += ATOM_INDEX_LEN; #define SEARCH_PAIRS(MATCH_CODE) \ while (npairs) { \ atom = GET_ATOM(cx, script, pc2); \ rval = ATOM_KEY(atom); \ MATCH_CODE \ if (match) { \ pc2 += ATOM_INDEX_LEN; \ len = GET_JUMP_OFFSET(pc2); \ DO_NEXT_OP(len); \ } \ pc2 += ATOM_INDEX_LEN + JUMP_OFFSET_LEN; \ npairs--; \ } if (JSVAL_IS_STRING(lval)) { str = JSVAL_TO_STRING(lval); SEARCH_PAIRS( match = (JSVAL_IS_STRING(rval) && ((str2 = JSVAL_TO_STRING(rval)) == str || js_EqualStrings(str2, str))); ) } else if (JSVAL_IS_DOUBLE(lval)) { d = *JSVAL_TO_DOUBLE(lval); SEARCH_PAIRS( match = (JSVAL_IS_DOUBLE(rval) && *JSVAL_TO_DOUBLE(rval) == d); ) } else { SEARCH_PAIRS( match = (lval == rval); ) } #undef SEARCH_PAIRS END_VARLEN_CASE BEGIN_CASE(JSOP_TABLESWITCHX) pc2 = pc; len = GET_JUMPX_OFFSET(pc2); /* * ECMAv2+ forbids conversion of discriminant, so we will skip to * the default case if the discriminant isn't already an int jsval. * (This opcode is emitted only for dense jsint-domain switches.) */ rval = POP_OPND(); if (!JSVAL_IS_INT(rval)) DO_NEXT_OP(len); i = JSVAL_TO_INT(rval); pc2 += JUMPX_OFFSET_LEN; low = GET_JUMP_OFFSET(pc2); pc2 += JUMP_OFFSET_LEN; high = GET_JUMP_OFFSET(pc2); i -= low; if ((jsuint)i < (jsuint)(high - low + 1)) { pc2 += JUMP_OFFSET_LEN + JUMPX_OFFSET_LEN * i; off = (jsint) GET_JUMPX_OFFSET(pc2); if (off) len = off; } END_VARLEN_CASE BEGIN_CASE(JSOP_LOOKUPSWITCHX) lval = POP_OPND(); pc2 = pc; len = GET_JUMPX_OFFSET(pc2); if (!JSVAL_IS_NUMBER(lval) && !JSVAL_IS_STRING(lval) && !JSVAL_IS_BOOLEAN(lval)) { DO_NEXT_OP(len); } pc2 += JUMPX_OFFSET_LEN; npairs = (jsint) GET_ATOM_INDEX(pc2); pc2 += ATOM_INDEX_LEN; #define SEARCH_EXTENDED_PAIRS(MATCH_CODE) \ while (npairs) { \ atom = GET_ATOM(cx, script, pc2); \ rval = ATOM_KEY(atom); \ MATCH_CODE \ if (match) { \ pc2 += ATOM_INDEX_LEN; \ len = GET_JUMPX_OFFSET(pc2); \ DO_NEXT_OP(len); \ } \ pc2 += ATOM_INDEX_LEN + JUMPX_OFFSET_LEN; \ npairs--; \ } if (JSVAL_IS_STRING(lval)) { str = JSVAL_TO_STRING(lval); SEARCH_EXTENDED_PAIRS( match = (JSVAL_IS_STRING(rval) && ((str2 = JSVAL_TO_STRING(rval)) == str || js_EqualStrings(str2, str))); ) } else if (JSVAL_IS_DOUBLE(lval)) { d = *JSVAL_TO_DOUBLE(lval); SEARCH_EXTENDED_PAIRS( match = (JSVAL_IS_DOUBLE(rval) && *JSVAL_TO_DOUBLE(rval) == d); ) } else { SEARCH_EXTENDED_PAIRS( match = (lval == rval); ) } #undef SEARCH_EXTENDED_PAIRS END_VARLEN_CASE EMPTY_CASE(JSOP_CONDSWITCH) #if JS_HAS_EXPORT_IMPORT BEGIN_CASE(JSOP_EXPORTALL) obj = fp->varobj; SAVE_SP_AND_PC(fp); ida = JS_Enumerate(cx, obj); if (!ida) { ok = JS_FALSE; } else { for (i = 0, j = ida->length; i < j; i++) { id = ida->vector[i]; ok = OBJ_LOOKUP_PROPERTY(cx, obj, id, &obj2, &prop); if (!ok) break; if (!prop) continue; ok = OBJ_GET_ATTRIBUTES(cx, obj, id, prop, &attrs); if (ok) { attrs |= JSPROP_EXPORTED; ok = OBJ_SET_ATTRIBUTES(cx, obj, id, prop, &attrs); } OBJ_DROP_PROPERTY(cx, obj2, prop); if (!ok) break; } JS_DestroyIdArray(cx, ida); } END_CASE(JSOP_EXPORTALL) BEGIN_LITOPX_CASE(JSOP_EXPORTNAME, 0) id = ATOM_TO_JSID(atom); obj = fp->varobj; SAVE_SP_AND_PC(fp); ok = OBJ_LOOKUP_PROPERTY(cx, obj, id, &obj2, &prop); if (!ok) goto out; if (!prop) { ok = OBJ_DEFINE_PROPERTY(cx, obj, id, JSVAL_VOID, NULL, NULL, JSPROP_EXPORTED, NULL); } else { ok = OBJ_GET_ATTRIBUTES(cx, obj, id, prop, &attrs); if (ok) { attrs |= JSPROP_EXPORTED; ok = OBJ_SET_ATTRIBUTES(cx, obj, id, prop, &attrs); } OBJ_DROP_PROPERTY(cx, obj2, prop); } if (!ok) goto out; END_LITOPX_CASE(JSOP_EXPORTNAME) BEGIN_CASE(JSOP_IMPORTALL) id = (jsid) JSVAL_VOID; PROPERTY_OP(-1, ok = ImportProperty(cx, obj, id)); sp--; END_CASE(JSOP_IMPORTALL) BEGIN_CASE(JSOP_IMPORTPROP) /* Get an immediate atom naming the property. */ atom = GET_ATOM(cx, script, pc); id = ATOM_TO_JSID(atom); PROPERTY_OP(-1, ok = ImportProperty(cx, obj, id)); sp--; END_CASE(JSOP_IMPORTPROP) BEGIN_CASE(JSOP_IMPORTELEM) ELEMENT_OP(-1, ok = ImportProperty(cx, obj, id)); sp -= 2; END_CASE(JSOP_IMPORTELEM) #endif /* JS_HAS_EXPORT_IMPORT */ BEGIN_CASE(JSOP_TRAP) SAVE_SP_AND_PC(fp); switch (JS_HandleTrap(cx, script, pc, &rval)) { case JSTRAP_ERROR: ok = JS_FALSE; goto out; case JSTRAP_CONTINUE: JS_ASSERT(JSVAL_IS_INT(rval)); op = (JSOp) JSVAL_TO_INT(rval); JS_ASSERT((uintN)op < (uintN)JSOP_LIMIT); LOAD_INTERRUPT_HANDLER(rt); DO_OP(); case JSTRAP_RETURN: fp->rval = rval; goto out; case JSTRAP_THROW: cx->throwing = JS_TRUE; cx->exception = rval; ok = JS_FALSE; goto out; default:; } LOAD_INTERRUPT_HANDLER(rt); END_CASE(JSOP_TRAP) BEGIN_CASE(JSOP_ARGUMENTS) SAVE_SP_AND_PC(fp); ok = js_GetArgsValue(cx, fp, &rval); if (!ok) goto out; PUSH_OPND(rval); obj = NULL; END_CASE(JSOP_ARGUMENTS) BEGIN_CASE(JSOP_ARGSUB) id = INT_TO_JSID(GET_ARGNO(pc)); SAVE_SP_AND_PC(fp); ok = js_GetArgsProperty(cx, fp, id, &obj, &rval); if (!ok) goto out; if (!obj) { /* * If arguments was not overridden by eval('arguments = ...'), * set obj to the magic cookie respected by JSOP_PUSHOBJ, just * in case this bytecode is part of an 'arguments[i](j, k)' or * similar such invocation sequence, where the function that * is invoked expects its 'this' parameter to be the caller's * arguments object. */ obj = LAZY_ARGS_THISP; } PUSH_OPND(rval); END_CASE(JSOP_ARGSUB) #undef LAZY_ARGS_THISP BEGIN_CASE(JSOP_ARGCNT) id = ATOM_TO_JSID(rt->atomState.lengthAtom); SAVE_SP_AND_PC(fp); ok = js_GetArgsProperty(cx, fp, id, &obj, &rval); if (!ok) goto out; PUSH_OPND(rval); END_CASE(JSOP_ARGCNT) BEGIN_CASE(JSOP_GETARG) slot = GET_ARGNO(pc); JS_ASSERT(slot < fp->fun->nargs); PUSH_OPND(fp->argv[slot]); obj = NULL; END_CASE(JSOP_GETARG) BEGIN_CASE(JSOP_SETARG) slot = GET_ARGNO(pc); JS_ASSERT(slot < fp->fun->nargs); vp = &fp->argv[slot]; GC_POKE(cx, *vp); *vp = FETCH_OPND(-1); obj = NULL; END_CASE(JSOP_SETARG) BEGIN_CASE(JSOP_GETVAR) slot = GET_VARNO(pc); JS_ASSERT(slot < fp->fun->u.i.nvars); PUSH_OPND(fp->vars[slot]); obj = NULL; END_CASE(JSOP_GETVAR) BEGIN_CASE(JSOP_SETVAR) slot = GET_VARNO(pc); JS_ASSERT(slot < fp->fun->u.i.nvars); vp = &fp->vars[slot]; GC_POKE(cx, *vp); *vp = FETCH_OPND(-1); obj = NULL; END_CASE(JSOP_SETVAR) BEGIN_CASE(JSOP_GETGVAR) slot = GET_VARNO(pc); JS_ASSERT(slot < fp->nvars); lval = fp->vars[slot]; if (JSVAL_IS_NULL(lval)) { op = JSOP_NAME; DO_OP(); } slot = JSVAL_TO_INT(lval); obj = fp->varobj; rval = OBJ_GET_SLOT(cx, obj, slot); PUSH_OPND(rval); END_CASE(JSOP_GETGVAR) BEGIN_CASE(JSOP_SETGVAR) slot = GET_VARNO(pc); JS_ASSERT(slot < fp->nvars); rval = FETCH_OPND(-1); lval = fp->vars[slot]; obj = fp->varobj; if (JSVAL_IS_NULL(lval)) { /* * Inline-clone and specialize JSOP_SETNAME code here because * JSOP_SETGVAR has arity 1: [rval], not arity 2: [obj, rval] * as JSOP_SETNAME does, where [obj] is due to JSOP_BINDNAME. */ atom = GET_ATOM(cx, script, pc); id = ATOM_TO_JSID(atom); SAVE_SP_AND_PC(fp); CACHED_SET(OBJ_SET_PROPERTY(cx, obj, id, &rval)); if (!ok) goto out; STORE_OPND(-1, rval); } else { slot = JSVAL_TO_INT(lval); GC_POKE(cx, obj->slots[slot]); OBJ_SET_SLOT(cx, obj, slot, rval); } obj = NULL; END_CASE(JSOP_SETGVAR) BEGIN_CASE(JSOP_DEFCONST) BEGIN_CASE(JSOP_DEFVAR) atomIndex = GET_ATOM_INDEX(pc); do_JSOP_DEFCONST: do_JSOP_DEFVAR: atom = js_GetAtom(cx, &script->atomMap, atomIndex); obj = fp->varobj; attrs = JSPROP_ENUMERATE; if (!(fp->flags & JSFRAME_EVAL)) attrs |= JSPROP_PERMANENT; if (op == JSOP_DEFCONST) attrs |= JSPROP_READONLY; /* Lookup id in order to check for redeclaration problems. */ id = ATOM_TO_JSID(atom); SAVE_SP_AND_PC(fp); ok = js_CheckRedeclaration(cx, obj, id, attrs, &obj2, &prop); if (!ok) goto out; /* Bind a variable only if it's not yet defined. */ if (!prop) { ok = OBJ_DEFINE_PROPERTY(cx, obj, id, JSVAL_VOID, NULL, NULL, attrs, &prop); if (!ok) goto out; JS_ASSERT(prop); obj2 = obj; } /* * Try to optimize a property we either just created, or found * directly in the global object, that is permanent, has a slot, * and has stub getter and setter, into a "fast global" accessed * by the JSOP_*GVAR opcodes. */ if (atomIndex < script->numGlobalVars && (attrs & JSPROP_PERMANENT) && obj2 == obj && OBJ_IS_NATIVE(obj)) { sprop = (JSScopeProperty *) prop; if (SPROP_HAS_VALID_SLOT(sprop, OBJ_SCOPE(obj)) && SPROP_HAS_STUB_GETTER(sprop) && SPROP_HAS_STUB_SETTER(sprop)) { /* * Fast globals use fp->vars to map the global name's * atomIndex to the permanent fp->varobj slot number, * tagged as a jsval. The atomIndex for the global's * name literal is identical to its fp->vars index. */ fp->vars[atomIndex] = INT_TO_JSVAL(sprop->slot); } } OBJ_DROP_PROPERTY(cx, obj2, prop); END_CASE(JSOP_DEFVAR) BEGIN_LITOPX_CASE(JSOP_DEFFUN, 0) obj = ATOM_TO_OBJECT(atom); fun = (JSFunction *) JS_GetPrivate(cx, obj); id = ATOM_TO_JSID(fun->atom); /* * We must be at top-level (either outermost block that forms a * function's body, or a global) scope, not inside an expression * (JSOP_{ANON,NAMED}FUNOBJ) or compound statement (JSOP_CLOSURE) * in the same compilation unit (ECMA Program). * * However, we could be in a Program being eval'd from inside a * with statement, so we need to distinguish scope chain head from * variables object. Hence the obj2 vs. parent distinction below. * First we make sure the function object we're defining has the * right scope chain. Then we define its name in fp->varobj. * * If static link is not current scope, clone fun's object to link * to the current scope via parent. This clause exists to enable * sharing of compiled functions among multiple equivalent scopes, * splitting the cost of compilation evenly among the scopes and * amortizing it over a number of executions. Examples include XUL * scripts and event handlers shared among Mozilla chrome windows, * and server-side JS user-defined functions shared among requests. * * NB: The Script object exposes compile and exec in the language, * such that this clause introduces an incompatible change from old * JS versions that supported Script. Such a JS version supported * executing a script that defined and called functions scoped by * the compile-time static link, not by the exec-time scope chain. * * We sacrifice compatibility, breaking such scripts, in order to * promote compile-cost sharing and amortizing, and because Script * is not and will not be standardized. */ JS_ASSERT(!fp->blockChain); obj2 = fp->scopeChain; if (OBJ_GET_PARENT(cx, obj) != obj2) { obj = js_CloneFunctionObject(cx, obj, obj2); if (!obj) { ok = JS_FALSE; goto out; } } /* * Protect obj from any GC hiding below OBJ_DEFINE_PROPERTY. All * paths from here must flow through the "Restore fp->scopeChain" * code below the OBJ_DEFINE_PROPERTY call. */ fp->scopeChain = obj; rval = OBJECT_TO_JSVAL(obj); /* * ECMA requires functions defined when entering Global code to be * permanent, and functions defined when entering Eval code to be * impermanent. */ attrs = JSPROP_ENUMERATE; if (!(fp->flags & JSFRAME_EVAL)) attrs |= JSPROP_PERMANENT; /* * Load function flags that are also property attributes. Getters * and setters do not need a slot, their value is stored elsewhere * in the property itself, not in obj->slots. */ flags = JSFUN_GSFLAG2ATTR(fun->flags); if (flags) { attrs |= flags | JSPROP_SHARED; rval = JSVAL_VOID; } /* * Check for a const property of the same name -- or any kind * of property if executing with the strict option. We check * here at runtime as well as at compile-time, to handle eval * as well as multiple HTML script tags. */ parent = fp->varobj; SAVE_SP_AND_PC(fp); ok = js_CheckRedeclaration(cx, parent, id, attrs, NULL, NULL); if (ok) { ok = OBJ_DEFINE_PROPERTY(cx, parent, id, rval, (flags & JSPROP_GETTER) ? JS_EXTENSION (JSPropertyOp) obj : NULL, (flags & JSPROP_SETTER) ? JS_EXTENSION (JSPropertyOp) obj : NULL, attrs, &prop); } /* Restore fp->scopeChain now that obj is defined in fp->varobj. */ fp->scopeChain = obj2; if (!ok) goto out; #if 0 if (attrs == (JSPROP_ENUMERATE | JSPROP_PERMANENT) && script->numGlobalVars) { /* * As with JSOP_DEFVAR and JSOP_DEFCONST (above), fast globals * use fp->vars to map the global function name's atomIndex to * its permanent fp->varobj slot number, tagged as a jsval. */ sprop = (JSScopeProperty *) prop; fp->vars[atomIndex] = INT_TO_JSVAL(sprop->slot); } #endif OBJ_DROP_PROPERTY(cx, parent, prop); END_LITOPX_CASE(JSOP_DEFFUN) BEGIN_LITOPX_CASE(JSOP_DEFLOCALFUN, VARNO_LEN) /* * Define a local function (i.e., one nested at the top level of * another function), parented by the current scope chain, and * stored in a local variable slot that the compiler allocated. * This is an optimization over JSOP_DEFFUN that avoids requiring * a call object for the outer function's activation. */ slot = GET_VARNO(pc2); obj = ATOM_TO_OBJECT(atom); JS_ASSERT(!fp->blockChain); if (!(fp->flags & JSFRAME_POP_BLOCKS)) { /* * If the compiler-created function object (obj) is scoped by a * let-induced body block, temporarily update fp->blockChain so * that js_GetScopeChain will clone the block into the runtime * scope needed to parent the function object's clone. */ parent = OBJ_GET_PARENT(cx, obj); if (OBJ_GET_CLASS(cx, parent) == &js_BlockClass) fp->blockChain = parent; parent = js_GetScopeChain(cx, fp); } else { /* * We have already emulated JSOP_ENTERBLOCK for the enclosing * body block, for a prior JSOP_DEFLOCALFUN in the prolog, so * we just load fp->scopeChain into parent. * * In typical execution scenarios, the prolog bytecodes that * include this JSOP_DEFLOCALFUN run, then come main bytecodes * including JSOP_ENTERBLOCK for the outermost (body) block. * JSOP_ENTERBLOCK will detect that it need not do anything if * the body block was entered above due to a local function. * Finally the matching JSOP_LEAVEBLOCK runs. * * If the matching JSOP_LEAVEBLOCK for the body block does not * run for some reason, the body block will be properly "put" * (via js_PutBlockObject) by the PutBlockObjects call at the * bottom of js_Interpret. */ parent = fp->scopeChain; JS_ASSERT(OBJ_GET_CLASS(cx, parent) == &js_BlockClass); JS_ASSERT(OBJ_GET_PROTO(cx, parent) == OBJ_GET_PARENT(cx, obj)); JS_ASSERT(OBJ_GET_CLASS(cx, OBJ_GET_PARENT(cx, parent)) == &js_CallClass); } /* If re-parenting, store a clone of the function object. */ if (OBJ_GET_PARENT(cx, obj) != parent) { SAVE_SP_AND_PC(fp); obj = js_CloneFunctionObject(cx, obj, parent); if (!obj) { ok = JS_FALSE; goto out; } } fp->vars[slot] = OBJECT_TO_JSVAL(obj); END_LITOPX_CASE(JSOP_DEFLOCALFUN) BEGIN_LITOPX_CASE(JSOP_ANONFUNOBJ, 0) /* Push the specified function object literal. */ obj = ATOM_TO_OBJECT(atom); /* If re-parenting, push a clone of the function object. */ SAVE_SP_AND_PC(fp); parent = js_GetScopeChain(cx, fp); if (!parent) { ok = JS_FALSE; goto out; } if (OBJ_GET_PARENT(cx, obj) != parent) { obj = js_CloneFunctionObject(cx, obj, parent); if (!obj) { ok = JS_FALSE; goto out; } } PUSH_OPND(OBJECT_TO_JSVAL(obj)); obj = NULL; END_LITOPX_CASE(JSOP_ANONFUNOBJ) BEGIN_LITOPX_CASE(JSOP_NAMEDFUNOBJ, 0) /* ECMA ed. 3 FunctionExpression: function Identifier [etc.]. */ rval = ATOM_KEY(atom); JS_ASSERT(VALUE_IS_FUNCTION(cx, rval)); /* * 1. Create a new object as if by the expression new Object(). * 2. Add Result(1) to the front of the scope chain. * * Step 2 is achieved by making the new object's parent be the * current scope chain, and then making the new object the parent * of the Function object clone. */ SAVE_SP_AND_PC(fp); obj2 = js_GetScopeChain(cx, fp); if (!obj2) { ok = JS_FALSE; goto out; } parent = js_NewObject(cx, &js_ObjectClass, NULL, obj2); if (!parent) { ok = JS_FALSE; goto out; } /* * 3. Create a new Function object as specified in section 13.2 * with [parameters and body specified by the function expression * that was parsed by the compiler into a Function object, and * saved in the script's atom map]. * * Protect parent from GC after js_CloneFunctionObject calls into * js_NewObject, which displaces the newborn object root in cx by * allocating the clone, then runs a last-ditch GC while trying * to allocate the clone's slots vector. Another, multi-threaded * path: js_CloneFunctionObject => js_NewObject => OBJ_GET_CLASS * which may suspend the current request in ClaimScope, with the * newborn displaced as in the first scenario. */ fp->scopeChain = parent; obj = js_CloneFunctionObject(cx, JSVAL_TO_OBJECT(rval), parent); if (!obj) { ok = JS_FALSE; goto out; } /* * Protect obj from any GC hiding below OBJ_DEFINE_PROPERTY. All * paths from here must flow through the "Restore fp->scopeChain" * code below the OBJ_DEFINE_PROPERTY call. */ fp->scopeChain = obj; rval = OBJECT_TO_JSVAL(obj); /* * 4. Create a property in the object Result(1). The property's * name is [fun->atom, the identifier parsed by the compiler], * value is Result(3), and attributes are { DontDelete, ReadOnly }. */ fun = (JSFunction *) JS_GetPrivate(cx, obj); attrs = JSFUN_GSFLAG2ATTR(fun->flags); if (attrs) { attrs |= JSPROP_SHARED; rval = JSVAL_VOID; } ok = OBJ_DEFINE_PROPERTY(cx, parent, ATOM_TO_JSID(fun->atom), rval, (attrs & JSPROP_GETTER) ? JS_EXTENSION (JSPropertyOp) obj : NULL, (attrs & JSPROP_SETTER) ? JS_EXTENSION (JSPropertyOp) obj : NULL, attrs | JSPROP_ENUMERATE | JSPROP_PERMANENT | JSPROP_READONLY, NULL); /* Restore fp->scopeChain now that obj is defined in parent. */ fp->scopeChain = obj2; if (!ok) { cx->weakRoots.newborn[GCX_OBJECT] = NULL; goto out; } /* * 5. Remove Result(1) from the front of the scope chain [no-op]. * 6. Return Result(3). */ PUSH_OPND(OBJECT_TO_JSVAL(obj)); obj = NULL; END_LITOPX_CASE(JSOP_NAMEDFUNOBJ) BEGIN_LITOPX_CASE(JSOP_CLOSURE, 0) /* * ECMA ed. 3 extension: a named function expression in a compound * statement (not at the top statement level of global code, or at * the top level of a function body). * * Get immediate operand atom, which is a function object literal. * From it, get the function to close. */ JS_ASSERT(VALUE_IS_FUNCTION(cx, ATOM_KEY(atom))); obj = ATOM_TO_OBJECT(atom); /* * Clone the function object with the current scope chain as the * clone's parent. The original function object is the prototype * of the clone. Do this only if re-parenting; the compiler may * have seen the right parent already and created a sufficiently * well-scoped function object. */ SAVE_SP_AND_PC(fp); obj2 = js_GetScopeChain(cx, fp); if (!obj2) { ok = JS_FALSE; goto out; } if (OBJ_GET_PARENT(cx, obj) != obj2) { obj = js_CloneFunctionObject(cx, obj, obj2); if (!obj) { ok = JS_FALSE; goto out; } } /* * Protect obj from any GC hiding below OBJ_DEFINE_PROPERTY. All * paths from here must flow through the "Restore fp->scopeChain" * code below the OBJ_DEFINE_PROPERTY call. */ fp->scopeChain = obj; rval = OBJECT_TO_JSVAL(obj); /* * Make a property in fp->varobj with id fun->atom and value obj, * unless fun is a getter or setter (in which case, obj is cast to * a JSPropertyOp and passed accordingly). */ fun = (JSFunction *) JS_GetPrivate(cx, obj); attrs = JSFUN_GSFLAG2ATTR(fun->flags); if (attrs) { attrs |= JSPROP_SHARED; rval = JSVAL_VOID; } parent = fp->varobj; ok = OBJ_DEFINE_PROPERTY(cx, parent, ATOM_TO_JSID(fun->atom), rval, (attrs & JSPROP_GETTER) ? JS_EXTENSION (JSPropertyOp) obj : NULL, (attrs & JSPROP_SETTER) ? JS_EXTENSION (JSPropertyOp) obj : NULL, attrs | JSPROP_ENUMERATE | JSPROP_PERMANENT, &prop); /* Restore fp->scopeChain now that obj is defined in fp->varobj. */ fp->scopeChain = obj2; if (!ok) { cx->weakRoots.newborn[GCX_OBJECT] = NULL; goto out; } #if 0 if (attrs == 0 && script->numGlobalVars) { /* * As with JSOP_DEFVAR and JSOP_DEFCONST (above), fast globals * use fp->vars to map the global function name's atomIndex to * its permanent fp->varobj slot number, tagged as a jsval. */ sprop = (JSScopeProperty *) prop; fp->vars[atomIndex] = INT_TO_JSVAL(sprop->slot); } #endif OBJ_DROP_PROPERTY(cx, parent, prop); END_LITOPX_CASE(JSOP_CLOSURE) #if JS_HAS_GETTER_SETTER BEGIN_CASE(JSOP_GETTER) BEGIN_CASE(JSOP_SETTER) op2 = (JSOp) *++pc; switch (op2) { case JSOP_SETNAME: case JSOP_SETPROP: atom = GET_ATOM(cx, script, pc); id = ATOM_TO_JSID(atom); rval = FETCH_OPND(-1); i = -1; goto gs_pop_lval; case JSOP_SETELEM: rval = FETCH_OPND(-1); FETCH_ELEMENT_ID(-2, id); i = -2; gs_pop_lval: FETCH_OBJECT(cx, i - 1, lval, obj); break; case JSOP_INITPROP: JS_ASSERT(sp - fp->spbase >= 2); rval = FETCH_OPND(-1); i = -1; atom = GET_ATOM(cx, script, pc); id = ATOM_TO_JSID(atom); goto gs_get_lval; case JSOP_INITELEM: JS_ASSERT(sp - fp->spbase >= 3); rval = FETCH_OPND(-1); FETCH_ELEMENT_ID(-2, id); i = -2; gs_get_lval: lval = FETCH_OPND(i-1); JS_ASSERT(JSVAL_IS_OBJECT(lval)); obj = JSVAL_TO_OBJECT(lval); break; default: JS_ASSERT(0); } /* Ensure that id has a type suitable for use with obj. */ CHECK_ELEMENT_ID(obj, id); SAVE_SP_AND_PC(fp); if (JS_TypeOfValue(cx, rval) != JSTYPE_FUNCTION) { JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_BAD_GETTER_OR_SETTER, (op == JSOP_GETTER) ? js_getter_str : js_setter_str); ok = JS_FALSE; goto out; } /* * Getters and setters are just like watchpoints from an access * control point of view. */ ok = OBJ_CHECK_ACCESS(cx, obj, id, JSACC_WATCH, &rtmp, &attrs); if (!ok) goto out; if (op == JSOP_GETTER) { getter = JS_EXTENSION (JSPropertyOp) JSVAL_TO_OBJECT(rval); setter = NULL; attrs = JSPROP_GETTER; } else { getter = NULL; setter = JS_EXTENSION (JSPropertyOp) JSVAL_TO_OBJECT(rval); attrs = JSPROP_SETTER; } attrs |= JSPROP_ENUMERATE | JSPROP_SHARED; /* Check for a readonly or permanent property of the same name. */ ok = js_CheckRedeclaration(cx, obj, id, attrs, NULL, NULL); if (!ok) goto out; ok = OBJ_DEFINE_PROPERTY(cx, obj, id, JSVAL_VOID, getter, setter, attrs, NULL); if (!ok) goto out; obj = NULL; sp += i; if (js_CodeSpec[op2].ndefs) STORE_OPND(-1, rval); len = js_CodeSpec[op2].length; DO_NEXT_OP(len); #endif /* JS_HAS_GETTER_SETTER */ BEGIN_CASE(JSOP_NEWINIT) argc = 0; fp->sharpDepth++; goto do_new; BEGIN_CASE(JSOP_ENDINIT) if (--fp->sharpDepth == 0) fp->sharpArray = NULL; /* Re-set the newborn root to the top of this object tree. */ JS_ASSERT(sp - fp->spbase >= 1); lval = FETCH_OPND(-1); JS_ASSERT(JSVAL_IS_OBJECT(lval)); cx->weakRoots.newborn[GCX_OBJECT] = JSVAL_TO_GCTHING(lval); END_CASE(JSOP_ENDINIT) BEGIN_CASE(JSOP_INITPROP) /* Pop the property's value into rval. */ JS_ASSERT(sp - fp->spbase >= 2); rval = FETCH_OPND(-1); /* Get the immediate property name into id. */ atom = GET_ATOM(cx, script, pc); id = ATOM_TO_JSID(atom); i = -1; goto do_init; BEGIN_CASE(JSOP_INITELEM) /* Pop the element's value into rval. */ JS_ASSERT(sp - fp->spbase >= 3); rval = FETCH_OPND(-1); /* Pop and conditionally atomize the element id. */ FETCH_ELEMENT_ID(-2, id); i = -2; do_init: /* Find the object being initialized at top of stack. */ lval = FETCH_OPND(i-1); JS_ASSERT(JSVAL_IS_OBJECT(lval)); obj = JSVAL_TO_OBJECT(lval); /* Ensure that id has a type suitable for use with obj. */ CHECK_ELEMENT_ID(obj, id); /* Set the property named by obj[id] to rval. */ SAVE_SP_AND_PC(fp); ok = OBJ_SET_PROPERTY(cx, obj, id, &rval); if (!ok) goto out; sp += i; len = js_CodeSpec[op].length; DO_NEXT_OP(len); #if JS_HAS_SHARP_VARS BEGIN_CASE(JSOP_DEFSHARP) SAVE_SP_AND_PC(fp); obj = fp->sharpArray; if (!obj) { obj = js_NewArrayObject(cx, 0, NULL); if (!obj) { ok = JS_FALSE; goto out; } fp->sharpArray = obj; } i = (jsint) GET_ATOM_INDEX(pc); id = INT_TO_JSID(i); rval = FETCH_OPND(-1); if (JSVAL_IS_PRIMITIVE(rval)) { char numBuf[12]; JS_snprintf(numBuf, sizeof numBuf, "%u", (unsigned) i); JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_BAD_SHARP_DEF, numBuf); ok = JS_FALSE; goto out; } ok = OBJ_SET_PROPERTY(cx, obj, id, &rval); if (!ok) goto out; END_CASE(JSOP_DEFSHARP) BEGIN_CASE(JSOP_USESHARP) i = (jsint) GET_ATOM_INDEX(pc); id = INT_TO_JSID(i); obj = fp->sharpArray; if (!obj) { rval = JSVAL_VOID; } else { SAVE_SP_AND_PC(fp); ok = OBJ_GET_PROPERTY(cx, obj, id, &rval); if (!ok) goto out; } if (!JSVAL_IS_OBJECT(rval)) { char numBuf[12]; JS_snprintf(numBuf, sizeof numBuf, "%u", (unsigned) i); SAVE_SP_AND_PC(fp); JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_BAD_SHARP_USE, numBuf); ok = JS_FALSE; goto out; } PUSH_OPND(rval); END_CASE(JSOP_USESHARP) #endif /* JS_HAS_SHARP_VARS */ /* No-ops for ease of decompilation and jit'ing. */ EMPTY_CASE(JSOP_TRY) EMPTY_CASE(JSOP_FINALLY) /* Reset the stack to the given depth. */ BEGIN_CASE(JSOP_SETSP) i = (jsint) GET_ATOM_INDEX(pc); JS_ASSERT(i >= 0); for (obj = fp->blockChain; obj; obj = OBJ_GET_PARENT(cx, obj)) { JS_ASSERT(OBJ_GET_CLASS(cx, obj) == &js_BlockClass); if (OBJ_BLOCK_DEPTH(cx, obj) + (jsint)OBJ_BLOCK_COUNT(cx, obj) <= i) { JS_ASSERT(OBJ_BLOCK_DEPTH(cx, obj) < i || OBJ_BLOCK_COUNT(cx, obj) == 0); break; } } fp->blockChain = obj; JS_ASSERT(ok); for (obj = fp->scopeChain; (clasp = OBJ_GET_CLASS(cx, obj)) == &js_WithClass || clasp == &js_BlockClass; obj = OBJ_GET_PARENT(cx, obj)) { if (JS_GetPrivate(cx, obj) != fp || OBJ_BLOCK_DEPTH(cx, obj) < i) { break; } if (clasp == &js_BlockClass) ok &= js_PutBlockObject(cx, obj); else JS_SetPrivate(cx, obj, NULL); } fp->scopeChain = obj; /* Set sp after js_PutBlockObject to avoid potential GC hazards. */ sp = fp->spbase + i; /* Don't fail until after we've updated all stacks. */ if (!ok) goto out; END_CASE(JSOP_SETSP) BEGIN_CASE(JSOP_GOSUB) JS_ASSERT(cx->exception != JSVAL_HOLE); if (!cx->throwing) { lval = JSVAL_HOLE; } else { lval = cx->exception; cx->throwing = JS_FALSE; } PUSH(lval); i = PTRDIFF(pc, script->main, jsbytecode) + JSOP_GOSUB_LENGTH; len = GET_JUMP_OFFSET(pc); PUSH(INT_TO_JSVAL(i)); END_VARLEN_CASE BEGIN_CASE(JSOP_GOSUBX) JS_ASSERT(cx->exception != JSVAL_HOLE); if (!cx->throwing) { lval = JSVAL_HOLE; } else { lval = cx->exception; cx->throwing = JS_FALSE; } PUSH(lval); i = PTRDIFF(pc, script->main, jsbytecode) + JSOP_GOSUBX_LENGTH; len = GET_JUMPX_OFFSET(pc); PUSH(INT_TO_JSVAL(i)); END_VARLEN_CASE BEGIN_CASE(JSOP_RETSUB) rval = POP(); JS_ASSERT(JSVAL_IS_INT(rval)); lval = POP(); if (lval != JSVAL_HOLE) { /* * Exception was pending during finally, throw it *before* we * adjust pc, because pc indexes into script->trynotes. This * turns out not to be necessary, but it seems clearer. And * it points out a FIXME: 350509, due to Igor Bukanov. */ cx->throwing = JS_TRUE; cx->exception = lval; ok = JS_FALSE; goto out; } len = JSVAL_TO_INT(rval); pc = script->main; END_VARLEN_CASE BEGIN_CASE(JSOP_EXCEPTION) JS_ASSERT(cx->throwing); PUSH(cx->exception); cx->throwing = JS_FALSE; END_CASE(JSOP_EXCEPTION) BEGIN_CASE(JSOP_THROWING) JS_ASSERT(!cx->throwing); cx->throwing = JS_TRUE; cx->exception = POP_OPND(); END_CASE(JSOP_THROWING) BEGIN_CASE(JSOP_THROW) JS_ASSERT(!cx->throwing); cx->throwing = JS_TRUE; cx->exception = POP_OPND(); ok = JS_FALSE; /* let the code at out try to catch the exception. */ goto out; BEGIN_CASE(JSOP_SETLOCALPOP) /* * The stack must have a block with at least one local slot below * the exception object. */ JS_ASSERT(sp - fp->spbase >= 2); slot = GET_UINT16(pc); JS_ASSERT(slot + 1 < (uintN)depth); fp->spbase[slot] = POP_OPND(); END_CASE(JSOP_SETLOCALPOP) BEGIN_CASE(JSOP_INSTANCEOF) SAVE_SP_AND_PC(fp); rval = FETCH_OPND(-1); if (JSVAL_IS_PRIMITIVE(rval) || !(obj = JSVAL_TO_OBJECT(rval))->map->ops->hasInstance) { str = js_DecompileValueGenerator(cx, -1, rval, NULL); if (str) { JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_BAD_INSTANCEOF_RHS, JS_GetStringBytes(str)); } ok = JS_FALSE; goto out; } lval = FETCH_OPND(-2); cond = JS_FALSE; ok = obj->map->ops->hasInstance(cx, obj, lval, &cond); if (!ok) goto out; sp--; STORE_OPND(-1, BOOLEAN_TO_JSVAL(cond)); END_CASE(JSOP_INSTANCEOF) #if JS_HAS_DEBUGGER_KEYWORD BEGIN_CASE(JSOP_DEBUGGER) { JSTrapHandler handler = rt->debuggerHandler; if (handler) { SAVE_SP_AND_PC(fp); switch (handler(cx, script, pc, &rval, rt->debuggerHandlerData)) { case JSTRAP_ERROR: ok = JS_FALSE; goto out; case JSTRAP_CONTINUE: break; case JSTRAP_RETURN: fp->rval = rval; goto out; case JSTRAP_THROW: cx->throwing = JS_TRUE; cx->exception = rval; ok = JS_FALSE; goto out; default:; } LOAD_INTERRUPT_HANDLER(rt); } } END_CASE(JSOP_DEBUGGER) #endif /* JS_HAS_DEBUGGER_KEYWORD */ #if JS_HAS_XML_SUPPORT BEGIN_CASE(JSOP_DEFXMLNS) rval = POP(); SAVE_SP_AND_PC(fp); ok = js_SetDefaultXMLNamespace(cx, rval); if (!ok) goto out; END_CASE(JSOP_DEFXMLNS) BEGIN_CASE(JSOP_ANYNAME) SAVE_SP_AND_PC(fp); ok = js_GetAnyName(cx, &rval); if (!ok) goto out; PUSH_OPND(rval); END_CASE(JSOP_ANYNAME) BEGIN_LITOPX_CASE(JSOP_QNAMEPART, 0) PUSH_OPND(ATOM_KEY(atom)); END_LITOPX_CASE(JSOP_QNAMEPART) BEGIN_LITOPX_CASE(JSOP_QNAMECONST, 0) rval = ATOM_KEY(atom); lval = FETCH_OPND(-1); SAVE_SP_AND_PC(fp); obj = js_ConstructXMLQNameObject(cx, lval, rval); if (!obj) { ok = JS_FALSE; goto out; } STORE_OPND(-1, OBJECT_TO_JSVAL(obj)); END_LITOPX_CASE(JSOP_QNAMECONST) BEGIN_CASE(JSOP_QNAME) rval = FETCH_OPND(-1); lval = FETCH_OPND(-2); SAVE_SP_AND_PC(fp); obj = js_ConstructXMLQNameObject(cx, lval, rval); if (!obj) { ok = JS_FALSE; goto out; } sp--; STORE_OPND(-1, OBJECT_TO_JSVAL(obj)); END_CASE(JSOP_QNAME) BEGIN_CASE(JSOP_TOATTRNAME) rval = FETCH_OPND(-1); SAVE_SP_AND_PC(fp); ok = js_ToAttributeName(cx, &rval); if (!ok) goto out; STORE_OPND(-1, rval); END_CASE(JSOP_TOATTRNAME) BEGIN_CASE(JSOP_TOATTRVAL) rval = FETCH_OPND(-1); JS_ASSERT(JSVAL_IS_STRING(rval)); SAVE_SP_AND_PC(fp); str = js_EscapeAttributeValue(cx, JSVAL_TO_STRING(rval)); if (!str) { ok = JS_FALSE; goto out; } STORE_OPND(-1, STRING_TO_JSVAL(str)); END_CASE(JSOP_TOATTRVAL) BEGIN_CASE(JSOP_ADDATTRNAME) BEGIN_CASE(JSOP_ADDATTRVAL) rval = FETCH_OPND(-1); lval = FETCH_OPND(-2); str = JSVAL_TO_STRING(lval); str2 = JSVAL_TO_STRING(rval); SAVE_SP_AND_PC(fp); str = js_AddAttributePart(cx, op == JSOP_ADDATTRNAME, str, str2); if (!str) { ok = JS_FALSE; goto out; } sp--; STORE_OPND(-1, STRING_TO_JSVAL(str)); END_CASE(JSOP_ADDATTRNAME) BEGIN_CASE(JSOP_BINDXMLNAME) lval = FETCH_OPND(-1); SAVE_SP_AND_PC(fp); ok = js_FindXMLProperty(cx, lval, &obj, &rval); if (!ok) goto out; STORE_OPND(-1, OBJECT_TO_JSVAL(obj)); PUSH_OPND(rval); END_CASE(JSOP_BINDXMLNAME) BEGIN_CASE(JSOP_SETXMLNAME) obj = JSVAL_TO_OBJECT(FETCH_OPND(-3)); lval = FETCH_OPND(-2); rval = FETCH_OPND(-1); SAVE_SP_AND_PC(fp); ok = js_SetXMLProperty(cx, obj, lval, &rval); if (!ok) goto out; sp -= 2; STORE_OPND(-1, rval); obj = NULL; END_CASE(JSOP_SETXMLNAME) BEGIN_CASE(JSOP_XMLNAME) lval = FETCH_OPND(-1); SAVE_SP_AND_PC(fp); ok = js_FindXMLProperty(cx, lval, &obj, &rval); if (!ok) goto out; ok = js_GetXMLProperty(cx, obj, rval, &rval); if (!ok) goto out; STORE_OPND(-1, rval); END_CASE(JSOP_XMLNAME) BEGIN_CASE(JSOP_DESCENDANTS) BEGIN_CASE(JSOP_DELDESC) FETCH_OBJECT(cx, -2, lval, obj); rval = FETCH_OPND(-1); SAVE_SP_AND_PC(fp); ok = js_GetXMLDescendants(cx, obj, rval, &rval); if (!ok) goto out; if (op == JSOP_DELDESC) { sp[-1] = rval; /* set local root */ ok = js_DeleteXMLListElements(cx, JSVAL_TO_OBJECT(rval)); if (!ok) goto out; rval = JSVAL_TRUE; /* always succeed */ } sp--; STORE_OPND(-1, rval); END_CASE(JSOP_DESCENDANTS) BEGIN_CASE(JSOP_FILTER) FETCH_OBJECT(cx, -1, lval, obj); len = GET_JUMP_OFFSET(pc); SAVE_SP_AND_PC(fp); ok = js_FilterXMLList(cx, obj, pc + js_CodeSpec[op].length, &rval); if (!ok) goto out; JS_ASSERT(fp->sp == sp); STORE_OPND(-1, rval); END_VARLEN_CASE BEGIN_CASE(JSOP_ENDFILTER) *result = POP_OPND(); goto out; EMPTY_CASE(JSOP_STARTXML) EMPTY_CASE(JSOP_STARTXMLEXPR) BEGIN_CASE(JSOP_TOXML) rval = FETCH_OPND(-1); SAVE_SP_AND_PC(fp); obj = js_ValueToXMLObject(cx, rval); if (!obj) { ok = JS_FALSE; goto out; } STORE_OPND(-1, OBJECT_TO_JSVAL(obj)); END_CASE(JSOP_TOXML) BEGIN_CASE(JSOP_TOXMLLIST) rval = FETCH_OPND(-1); SAVE_SP_AND_PC(fp); obj = js_ValueToXMLListObject(cx, rval); if (!obj) { ok = JS_FALSE; goto out; } STORE_OPND(-1, OBJECT_TO_JSVAL(obj)); END_CASE(JSOP_TOXMLLIST) BEGIN_CASE(JSOP_XMLTAGEXPR) rval = FETCH_OPND(-1); SAVE_SP_AND_PC(fp); str = js_ValueToString(cx, rval); if (!str) { ok = JS_FALSE; goto out; } STORE_OPND(-1, STRING_TO_JSVAL(str)); END_CASE(JSOP_XMLTAGEXPR) BEGIN_CASE(JSOP_XMLELTEXPR) rval = FETCH_OPND(-1); SAVE_SP_AND_PC(fp); if (VALUE_IS_XML(cx, rval)) { str = js_ValueToXMLString(cx, rval); } else { str = js_ValueToString(cx, rval); if (str) str = js_EscapeElementValue(cx, str); } if (!str) { ok = JS_FALSE; goto out; } STORE_OPND(-1, STRING_TO_JSVAL(str)); END_CASE(JSOP_XMLELTEXPR) BEGIN_LITOPX_CASE(JSOP_XMLOBJECT, 0) SAVE_SP_AND_PC(fp); obj = js_CloneXMLObject(cx, ATOM_TO_OBJECT(atom)); if (!obj) { ok = JS_FALSE; goto out; } PUSH_OPND(OBJECT_TO_JSVAL(obj)); obj = NULL; END_LITOPX_CASE(JSOP_XMLOBJECT) BEGIN_LITOPX_CASE(JSOP_XMLCDATA, 0) str = ATOM_TO_STRING(atom); obj = js_NewXMLSpecialObject(cx, JSXML_CLASS_TEXT, NULL, str); if (!obj) { ok = JS_FALSE; goto out; } PUSH_OPND(OBJECT_TO_JSVAL(obj)); END_LITOPX_CASE(JSOP_XMLCDATA) BEGIN_LITOPX_CASE(JSOP_XMLCOMMENT, 0) str = ATOM_TO_STRING(atom); obj = js_NewXMLSpecialObject(cx, JSXML_CLASS_COMMENT, NULL, str); if (!obj) { ok = JS_FALSE; goto out; } PUSH_OPND(OBJECT_TO_JSVAL(obj)); END_LITOPX_CASE(JSOP_XMLCOMMENT) BEGIN_LITOPX_CASE(JSOP_XMLPI, 0) str = ATOM_TO_STRING(atom); rval = FETCH_OPND(-1); str2 = JSVAL_TO_STRING(rval); SAVE_SP_AND_PC(fp); obj = js_NewXMLSpecialObject(cx, JSXML_CLASS_PROCESSING_INSTRUCTION, str, str2); if (!obj) { ok = JS_FALSE; goto out; } STORE_OPND(-1, OBJECT_TO_JSVAL(obj)); END_LITOPX_CASE(JSOP_XMLPI) BEGIN_LITOPX_CASE(JSOP_GETMETHOD, 0) /* Get an immediate atom naming the property. */ id = ATOM_TO_JSID(atom); lval = FETCH_OPND(-1); SAVE_SP_AND_PC(fp); if (!JSVAL_IS_PRIMITIVE(lval)) { STORE_OPND(-1, lval); obj = JSVAL_TO_OBJECT(lval); /* Special-case XML object method lookup, per ECMA-357. */ if (OBJECT_IS_XML(cx, obj)) { JSXMLObjectOps *ops; ops = (JSXMLObjectOps *) obj->map->ops; obj = ops->getMethod(cx, obj, id, &rval); if (!obj) ok = JS_FALSE; } else { CACHED_GET(OBJ_GET_PROPERTY(cx, obj, id, &rval)); } } else { if (JSVAL_IS_STRING(lval)) { i = JSProto_String; } else if (JSVAL_IS_NUMBER(lval)) { i = JSProto_Number; } else if (JSVAL_IS_BOOLEAN(lval)) { i = JSProto_Boolean; } else { JS_ASSERT(JSVAL_IS_NULL(lval) || JSVAL_IS_VOID(lval)); str = js_DecompileValueGenerator(cx, JSDVG_SEARCH_STACK, lval, NULL); if (str) { JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_NO_PROPERTIES, JS_GetStringBytes(str)); } ok = JS_FALSE; goto out; } ok = js_GetClassPrototype(cx, NULL, INT_TO_JSID(i), &obj); if (!ok) goto out; JS_ASSERT(obj); STORE_OPND(-1, OBJECT_TO_JSVAL(obj)); CACHED_GET(OBJ_GET_PROPERTY(cx, obj, id, &rval)); obj = (JSObject *) lval; /* keep tagged as non-object */ } if (!ok) goto out; STORE_OPND(-1, rval); END_LITOPX_CASE(JSOP_GETMETHOD) BEGIN_LITOPX_CASE(JSOP_SETMETHOD, 0) /* Get an immediate atom naming the property. */ id = ATOM_TO_JSID(atom); rval = FETCH_OPND(-1); FETCH_OBJECT(cx, -2, lval, obj); SAVE_SP_AND_PC(fp); /* Special-case XML object method lookup, per ECMA-357. */ if (OBJECT_IS_XML(cx, obj)) { JSXMLObjectOps *ops; ops = (JSXMLObjectOps *) obj->map->ops; ok = ops->setMethod(cx, obj, id, &rval); } else { CACHED_SET(OBJ_SET_PROPERTY(cx, obj, id, &rval)); } if (!ok) goto out; --sp; STORE_OPND(-1, rval); obj = NULL; END_LITOPX_CASE(JSOP_SETMETHOD) BEGIN_CASE(JSOP_GETFUNNS) SAVE_SP_AND_PC(fp); ok = js_GetFunctionNamespace(cx, &rval); if (!ok) goto out; PUSH_OPND(rval); END_CASE(JSOP_GETFUNNS) #endif /* JS_HAS_XML_SUPPORT */ BEGIN_LITOPX_CASE(JSOP_ENTERBLOCK, 0) obj = ATOM_TO_OBJECT(atom); JS_ASSERT(fp->spbase + OBJ_BLOCK_DEPTH(cx, obj) == sp); vp = sp + OBJ_BLOCK_COUNT(cx, obj); JS_ASSERT(vp <= fp->spbase + depth); while (sp < vp) { STORE_OPND(0, JSVAL_VOID); sp++; } /* * If this frame had to reflect the compile-time block chain into * the runtime scope chain, we can't optimize block scopes out of * runtime any longer, because an outer block that parents obj has * been cloned onto the scope chain. To avoid re-cloning such a * parent and accumulating redundant clones via js_GetScopeChain, * we must clone each block eagerly on entry, and push it on the * scope chain, until this frame pops. */ if (fp->flags & JSFRAME_POP_BLOCKS) { JS_ASSERT(!fp->blockChain); /* * Check whether JSOP_DEFLOCALFUN emulated JSOP_ENTERBLOCK for * the body block in order to correctly scope the local cloned * function object it creates. */ parent = fp->scopeChain; if (OBJ_GET_PROTO(cx, parent) == obj) { JS_ASSERT(OBJ_GET_CLASS(cx, parent) == &js_BlockClass); } else { obj = js_CloneBlockObject(cx, obj, parent, fp); if (!obj) { ok = JS_FALSE; goto out; } fp->scopeChain = obj; } } else { JS_ASSERT(!fp->blockChain || OBJ_GET_PARENT(cx, obj) == fp->blockChain); fp->blockChain = obj; } END_LITOPX_CASE(JSOP_ENTERBLOCK) BEGIN_CASE(JSOP_LEAVEBLOCKEXPR) BEGIN_CASE(JSOP_LEAVEBLOCK) { JSObject **chainp; /* Grab the result of the expression. */ if (op == JSOP_LEAVEBLOCKEXPR) rval = FETCH_OPND(-1); chainp = &fp->blockChain; obj = *chainp; if (!obj) { chainp = &fp->scopeChain; obj = *chainp; /* * This block was cloned, so clear its private data and sync * its locals to their property slots. */ SAVE_SP_AND_PC(fp); ok = js_PutBlockObject(cx, obj); if (!ok) goto out; } sp -= GET_UINT16(pc); JS_ASSERT(fp->spbase <= sp && sp <= fp->spbase + depth); /* Store the result into the topmost stack slot. */ if (op == JSOP_LEAVEBLOCKEXPR) STORE_OPND(-1, rval); JS_ASSERT(OBJ_GET_CLASS(cx, obj) == &js_BlockClass); JS_ASSERT(op == JSOP_LEAVEBLOCKEXPR ? fp->spbase + OBJ_BLOCK_DEPTH(cx, obj) == sp - 1 : fp->spbase + OBJ_BLOCK_DEPTH(cx, obj) == sp); *chainp = OBJ_GET_PARENT(cx, obj); JS_ASSERT(chainp != &fp->blockChain || !*chainp || OBJ_GET_CLASS(cx, *chainp) == &js_BlockClass); } END_CASE(JSOP_LEAVEBLOCK) BEGIN_CASE(JSOP_GETLOCAL) slot = GET_UINT16(pc); JS_ASSERT(slot < (uintN)depth); PUSH_OPND(fp->spbase[slot]); obj = NULL; END_CASE(JSOP_GETLOCAL) BEGIN_CASE(JSOP_SETLOCAL) slot = GET_UINT16(pc); JS_ASSERT(slot < (uintN)depth); vp = &fp->spbase[slot]; GC_POKE(cx, *vp); *vp = FETCH_OPND(-1); obj = NULL; END_CASE(JSOP_SETLOCAL) /* NB: This macro doesn't use JS_BEGIN_MACRO/JS_END_MACRO around its body. */ #define FAST_LOCAL_INCREMENT_OP(PRE,OPEQ,MINMAX) \ slot = GET_UINT16(pc); \ JS_ASSERT(slot < (uintN)depth); \ vp = fp->spbase + slot; \ rval = *vp; \ if (!JSVAL_IS_INT(rval) || rval == INT_TO_JSVAL(JSVAL_INT_##MINMAX)) \ goto do_nonint_fast_incop; \ PRE = rval; \ rval OPEQ 2; \ *vp = rval; \ PUSH_OPND(PRE) BEGIN_CASE(JSOP_INCLOCAL) FAST_LOCAL_INCREMENT_OP(rval, +=, MAX); END_CASE(JSOP_INCLOCAL) BEGIN_CASE(JSOP_DECLOCAL) FAST_LOCAL_INCREMENT_OP(rval, -=, MIN); END_CASE(JSOP_DECLOCAL) BEGIN_CASE(JSOP_LOCALINC) FAST_LOCAL_INCREMENT_OP(rtmp, +=, MAX); END_CASE(JSOP_LOCALINC) BEGIN_CASE(JSOP_LOCALDEC) FAST_LOCAL_INCREMENT_OP(rtmp, -=, MIN); END_CASE(JSOP_LOCALDEC) #undef FAST_LOCAL_INCREMENT_OP EMPTY_CASE(JSOP_STARTITER) BEGIN_CASE(JSOP_ENDITER) JS_ASSERT(!JSVAL_IS_PRIMITIVE(sp[-1])); iterobj = JSVAL_TO_OBJECT(sp[-1]); /* * js_CloseNativeIterator checks whether the iterator is not * native, and also detects the case of a native iterator that * has already escaped, even though a for-in loop caused it to * be created. See jsiter.c. */ SAVE_SP_AND_PC(fp); js_CloseNativeIterator(cx, iterobj); *--sp = JSVAL_NULL; END_CASE(JSOP_ENDITER) #if JS_HAS_GENERATORS BEGIN_CASE(JSOP_GENERATOR) pc += JSOP_GENERATOR_LENGTH; SAVE_SP_AND_PC(fp); obj = js_NewGenerator(cx, fp); if (!obj) { ok = JS_FALSE; } else { JS_ASSERT(!fp->callobj && !fp->argsobj); fp->rval = OBJECT_TO_JSVAL(obj); } goto out; BEGIN_CASE(JSOP_YIELD) ASSERT_NOT_THROWING(cx); if (fp->flags & JSFRAME_FILTERING) { /* FIXME: bug 309894 -- fix to eliminate this error. */ JS_ReportErrorNumberUC(cx, js_GetErrorMessage, NULL, JSMSG_YIELD_FROM_FILTER); ok = JS_FALSE; goto out; } if (FRAME_TO_GENERATOR(fp)->state == JSGEN_CLOSING) { str = js_DecompileValueGenerator(cx, JSDVG_SEARCH_STACK, fp->argv[-2], NULL); if (str) { JS_ReportErrorNumberUC(cx, js_GetErrorMessage, NULL, JSMSG_BAD_GENERATOR_YIELD, JSSTRING_CHARS(str)); } ok = JS_FALSE; goto out; } fp->rval = FETCH_OPND(-1); fp->flags |= JSFRAME_YIELDING; pc += JSOP_YIELD_LENGTH; SAVE_SP_AND_PC(fp); goto out; BEGIN_CASE(JSOP_ARRAYPUSH) slot = GET_UINT16(pc); JS_ASSERT(slot < (uintN)depth); lval = fp->spbase[slot]; obj = JSVAL_TO_OBJECT(lval); JS_ASSERT(OBJ_GET_CLASS(cx, obj) == &js_ArrayClass); rval = FETCH_OPND(-1); /* We know that the array is created with only a 'length' slot. */ i = obj->map->freeslot - (JSSLOT_FREE(&js_ArrayClass) + 1); id = INT_TO_JSID(i); SAVE_SP_AND_PC(fp); ok = OBJ_SET_PROPERTY(cx, obj, id, &rval); if (!ok) goto out; --sp; END_CASE(JSOP_ARRAYPUSH) #endif /* JS_HAS_GENERATORS */ #if !JS_HAS_GENERATORS L_JSOP_GENERATOR: L_JSOP_YIELD: L_JSOP_ARRAYPUSH: #endif #if !JS_HAS_DESTRUCTURING L_JSOP_FOREACHKEYVAL: L_JSOP_ENUMCONSTELEM: #endif #ifdef JS_THREADED_INTERP L_JSOP_BACKPATCH: L_JSOP_BACKPATCH_POP: #else default: #endif { char numBuf[12]; JS_snprintf(numBuf, sizeof numBuf, "%d", op); JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_BAD_BYTECODE, numBuf); ok = JS_FALSE; goto out; } #ifndef JS_THREADED_INTERP } /* switch (op) */ advance_pc: pc += len; #ifdef DEBUG if (tracefp) { intN ndefs, n; jsval *siter; ndefs = js_CodeSpec[op].ndefs; if (ndefs) { SAVE_SP_AND_PC(fp); if (op == JSOP_FORELEM && sp[-1] == JSVAL_FALSE) --ndefs; for (n = -ndefs; n < 0; n++) { str = js_DecompileValueGenerator(cx, n, sp[n], NULL); if (str) { fprintf(tracefp, "%s %s", (n == -ndefs) ? " output:" : ",", JS_GetStringBytes(str)); } } fprintf(tracefp, " @ %d\n", sp - fp->spbase); } fprintf(tracefp, " stack: "); for (siter = fp->spbase; siter < sp; siter++) { str = js_ValueToSource(cx, *siter); fprintf(tracefp, "%s ", str ? JS_GetStringBytes(str) : ""); } fputc('\n', tracefp); } #endif /* DEBUG */ } #endif /* !JS_THREADED_INTERP */ out: if (!ok) { /* * Has an exception been raised? Also insist that we are not in an * XML filtering predicate expression, to avoid catching exceptions * within the filtering predicate, such as this example taken from * tests/e4x/Regress/regress-301596.js: * * try { * .(@a == 1); * throw 5; * } catch (e) { * } * * The inner interpreter activation executing the predicate bytecode * will throw "reference to undefined XML name @a" (or 5, in older * versions that followed the first edition of ECMA-357 and evaluated * unbound identifiers to undefined), and the exception must not be * caught until control unwinds to the outer interpreter activation. * * Otherwise, the wrong stack depth will be restored by JSOP_SETSP, * and the catch will move into the filtering predicate expression, * leading to double catch execution if it rethrows. * * FIXME: https://bugzilla.mozilla.org/show_bug.cgi?id=309894 */ if (cx->throwing && !(fp->flags & JSFRAME_FILTERING)) { /* * Call debugger throw hook if set (XXX thread safety?). */ JSTrapHandler handler = rt->throwHook; if (handler) { SAVE_SP_AND_PC(fp); switch (handler(cx, script, pc, &rval, rt->throwHookData)) { case JSTRAP_ERROR: cx->throwing = JS_FALSE; goto no_catch; case JSTRAP_RETURN: ok = JS_TRUE; cx->throwing = JS_FALSE; fp->rval = rval; goto no_catch; case JSTRAP_THROW: cx->exception = rval; case JSTRAP_CONTINUE: default:; } LOAD_INTERRUPT_HANDLER(rt); } /* * Look for a try block in script that can catch this exception. */ #if JS_HAS_GENERATORS if (JS_LIKELY(cx->exception != JSVAL_ARETURN)) { SCRIPT_FIND_CATCH_START(script, pc, pc); if (!pc) goto no_catch; } else { pc = js_FindFinallyHandler(script, pc); if (!pc) { cx->throwing = JS_FALSE; ok = JS_TRUE; fp->rval = JSVAL_VOID; goto no_catch; } } #else SCRIPT_FIND_CATCH_START(script, pc, pc); if (!pc) goto no_catch; #endif /* Don't clear cx->throwing to save cx->exception from GC. */ len = 0; ok = JS_TRUE; DO_NEXT_OP(len); } no_catch:; } /* * Check whether control fell off the end of a lightweight function, or an * exception thrown under such a function was not caught by it. If so, go * to the inline code under JSOP_RETURN. */ if (inlineCallCount) goto inline_return; /* * Reset sp before freeing stack slots, because our caller may GC soon. * Clear spbase to indicate that we've popped the 2 * depth operand slots. * Restore the previous frame's execution state. */ if (JS_LIKELY(mark != NULL)) { /* If fp has blocks on its scope chain, home their locals now. */ if (fp->flags & JSFRAME_POP_BLOCKS) { SAVE_SP_AND_PC(fp); ok &= PutBlockObjects(cx, fp); } fp->sp = fp->spbase; fp->spbase = NULL; js_FreeRawStack(cx, mark); } else { SAVE_SP(fp); } out2: if (cx->version == currentVersion && currentVersion != originalVersion) js_SetVersion(cx, originalVersion); cx->interpLevel--; return ok; atom_not_defined: { const char *printable = js_AtomToPrintableString(cx, atom); if (printable) js_ReportIsNotDefined(cx, printable); ok = JS_FALSE; goto out; } } pacparser-1.4.5/src/spidermonkey/js/src/jsinterp.h000066400000000000000000000370251464010763600222310ustar00rootroot00000000000000/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- * * ***** BEGIN LICENSE BLOCK ***** * Version: MPL 1.1/GPL 2.0/LGPL 2.1 * * The contents of this file are subject to the Mozilla Public License Version * 1.1 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * http://www.mozilla.org/MPL/ * * Software distributed under the License is distributed on an "AS IS" basis, * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License * for the specific language governing rights and limitations under the * License. * * The Original Code is Mozilla Communicator client code, released * March 31, 1998. * * The Initial Developer of the Original Code is * Netscape Communications Corporation. * Portions created by the Initial Developer are Copyright (C) 1998 * the Initial Developer. All Rights Reserved. * * Contributor(s): * * Alternatively, the contents of this file may be used under the terms of * either of the GNU General Public License Version 2 or later (the "GPL"), * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), * in which case the provisions of the GPL or the LGPL are applicable instead * of those above. If you wish to allow use of your version of this file only * under the terms of either the GPL or the LGPL, and not to allow others to * use your version of this file under the terms of the MPL, indicate your * decision by deleting the provisions above and replace them with the notice * and other provisions required by the GPL or the LGPL. If you do not delete * the provisions above, a recipient may use your version of this file under * the terms of any one of the MPL, the GPL or the LGPL. * * ***** END LICENSE BLOCK ***** */ #ifndef jsinterp_h___ #define jsinterp_h___ /* * JS interpreter interface. */ #include "jsprvtd.h" #include "jspubtd.h" JS_BEGIN_EXTERN_C /* * JS stack frame, may be allocated on the C stack by native callers. Always * allocated on cx->stackPool for calls from the interpreter to an interpreted * function. * * NB: This struct is manually initialized in jsinterp.c and jsiter.c. If you * add new members, update both files. But first, try to remove members. The * sharp* and xml* members should be moved onto the stack as local variables * with well-known slots, if possible. */ struct JSStackFrame { JSObject *callobj; /* lazily created Call object */ JSObject *argsobj; /* lazily created arguments object */ JSObject *varobj; /* variables object, where vars go */ JSScript *script; /* script being interpreted */ JSFunction *fun; /* function being called or null */ JSObject *thisp; /* "this" pointer if in method */ uintN argc; /* actual argument count */ jsval *argv; /* base of argument stack slots */ jsval rval; /* function return value */ uintN nvars; /* local variable count */ jsval *vars; /* base of variable stack slots */ JSStackFrame *down; /* previous frame */ void *annotation; /* used by Java security */ JSObject *scopeChain; /* scope chain */ jsbytecode *pc; /* program counter */ jsval *sp; /* stack pointer */ jsval *spbase; /* operand stack base */ uintN sharpDepth; /* array/object initializer depth */ JSObject *sharpArray; /* scope for #n= initializer vars */ uint32 flags; /* frame flags -- see below */ JSStackFrame *dormantNext; /* next dormant frame chain */ JSObject *xmlNamespace; /* null or default xml namespace in E4X */ JSObject *blockChain; /* active compile-time block scopes */ }; typedef struct JSInlineFrame { JSStackFrame frame; /* base struct */ jsval *rvp; /* ptr to caller's return value slot */ void *mark; /* mark before inline frame */ void *hookData; /* debugger call hook data */ JSVersion callerVersion; /* dynamic version of calling script */ } JSInlineFrame; /* JS stack frame flags. */ #define JSFRAME_CONSTRUCTING 0x01 /* frame is for a constructor invocation */ #define JSFRAME_INTERNAL 0x02 /* internal call, not invoked by a script */ #define JSFRAME_SKIP_CALLER 0x04 /* skip one link when evaluating f.caller for this invocation of f */ #define JSFRAME_ASSIGNING 0x08 /* a complex (not simplex JOF_ASSIGNING) op is currently assigning to a property */ #define JSFRAME_DEBUGGER 0x10 /* frame for JS_EvaluateInStackFrame */ #define JSFRAME_EVAL 0x20 /* frame for obj_eval */ #define JSFRAME_SPECIAL 0x30 /* special evaluation frame flags */ #define JSFRAME_COMPILING 0x40 /* frame is being used by compiler */ #define JSFRAME_COMPILE_N_GO 0x80 /* compiler-and-go mode, can optimize name references based on scope chain */ #define JSFRAME_SCRIPT_OBJECT 0x100 /* compiling source for a Script object */ #define JSFRAME_YIELDING 0x200 /* js_Interpret dispatched JSOP_YIELD */ #define JSFRAME_FILTERING 0x400 /* XML filtering predicate expression */ #define JSFRAME_ITERATOR 0x800 /* trying to get an iterator for for-in */ #define JSFRAME_POP_BLOCKS 0x1000 /* scope chain contains blocks to pop */ #define JSFRAME_GENERATOR 0x2000 /* frame belongs to generator-iterator */ #define JSFRAME_OVERRIDE_SHIFT 24 /* override bit-set params; see jsfun.c */ #define JSFRAME_OVERRIDE_BITS 8 /* * Property cache for quickened get/set property opcodes. */ #define PROPERTY_CACHE_LOG2 10 #define PROPERTY_CACHE_SIZE JS_BIT(PROPERTY_CACHE_LOG2) #define PROPERTY_CACHE_MASK JS_BITMASK(PROPERTY_CACHE_LOG2) #define PROPERTY_CACHE_HASH(obj, id) \ ((((jsuword)(obj) >> JSVAL_TAGBITS) ^ (jsuword)(id)) & PROPERTY_CACHE_MASK) #ifdef JS_THREADSAFE #if HAVE_ATOMIC_DWORD_ACCESS #define PCE_LOAD(cache, pce, entry) JS_ATOMIC_DWORD_LOAD(pce, entry) #define PCE_STORE(cache, pce, entry) JS_ATOMIC_DWORD_STORE(pce, entry) #else /* !HAVE_ATOMIC_DWORD_ACCESS */ #define JS_PROPERTY_CACHE_METERING 1 #define PCE_LOAD(cache, pce, entry) \ JS_BEGIN_MACRO \ uint32 prefills_; \ uint32 fills_ = (cache)->fills; \ do { \ /* Load until cache->fills is stable (see FILL macro below). */ \ prefills_ = fills_; \ (entry) = *(pce); \ } while ((fills_ = (cache)->fills) != prefills_); \ JS_END_MACRO #define PCE_STORE(cache, pce, entry) \ JS_BEGIN_MACRO \ do { \ /* Store until no racing collider stores half or all of pce. */ \ *(pce) = (entry); \ } while (PCE_OBJECT(*pce) != PCE_OBJECT(entry) || \ PCE_PROPERTY(*pce) != PCE_PROPERTY(entry)); \ JS_END_MACRO #endif /* !HAVE_ATOMIC_DWORD_ACCESS */ #else /* !JS_THREADSAFE */ #define PCE_LOAD(cache, pce, entry) ((entry) = *(pce)) #define PCE_STORE(cache, pce, entry) (*(pce) = (entry)) #endif /* !JS_THREADSAFE */ typedef union JSPropertyCacheEntry { struct { JSObject *object; /* weak link to object */ JSScopeProperty *property; /* weak link to property */ } s; #ifdef HAVE_ATOMIC_DWORD_ACCESS prdword align; #endif } JSPropertyCacheEntry; /* These may be called in lvalue or rvalue position. */ #define PCE_OBJECT(entry) ((entry).s.object) #define PCE_PROPERTY(entry) ((entry).s.property) typedef struct JSPropertyCache { JSPropertyCacheEntry table[PROPERTY_CACHE_SIZE]; JSBool empty; JSBool disabled; #ifdef JS_PROPERTY_CACHE_METERING uint32 fills; uint32 recycles; uint32 tests; uint32 misses; uint32 flushes; # define PCMETER(x) x #else # define PCMETER(x) /* nothing */ #endif } JSPropertyCache; #define PROPERTY_CACHE_FILL(cache, obj, id, sprop) \ JS_BEGIN_MACRO \ JSPropertyCache *cache_ = (cache); \ if (!cache_->disabled) { \ uintN hashIndex_ = (uintN) PROPERTY_CACHE_HASH(obj, id); \ JSPropertyCacheEntry *pce_ = &cache_->table[hashIndex_]; \ JSPropertyCacheEntry entry_; \ JSScopeProperty *pce_sprop_; \ PCE_LOAD(cache_, pce_, entry_); \ pce_sprop_ = PCE_PROPERTY(entry_); \ PCMETER(if (pce_sprop_ && pce_sprop_ != sprop) \ cache_->recycles++); \ PCE_OBJECT(entry_) = obj; \ PCE_PROPERTY(entry_) = sprop; \ cache_->empty = JS_FALSE; \ PCMETER(cache_->fills++); \ PCE_STORE(cache_, pce_, entry_); \ } \ JS_END_MACRO #define PROPERTY_CACHE_TEST(cache, obj, id, sprop) \ JS_BEGIN_MACRO \ uintN hashIndex_ = (uintN) PROPERTY_CACHE_HASH(obj, id); \ JSPropertyCache *cache_ = (cache); \ JSPropertyCacheEntry *pce_ = &cache_->table[hashIndex_]; \ JSPropertyCacheEntry entry_; \ JSScopeProperty *pce_sprop_; \ PCE_LOAD(cache_, pce_, entry_); \ pce_sprop_ = PCE_PROPERTY(entry_); \ PCMETER(cache_->tests++); \ if (pce_sprop_ && \ PCE_OBJECT(entry_) == obj && \ pce_sprop_->id == id) { \ sprop = pce_sprop_; \ } else { \ PCMETER(cache_->misses++); \ sprop = NULL; \ } \ JS_END_MACRO extern void js_FlushPropertyCache(JSContext *cx); extern void js_DisablePropertyCache(JSContext *cx); extern void js_EnablePropertyCache(JSContext *cx); extern JS_FRIEND_API(jsval *) js_AllocStack(JSContext *cx, uintN nslots, void **markp); extern JS_FRIEND_API(void) js_FreeStack(JSContext *cx, void *mark); extern JSBool js_GetArgument(JSContext *cx, JSObject *obj, jsval id, jsval *vp); extern JSBool js_SetArgument(JSContext *cx, JSObject *obj, jsval id, jsval *vp); extern JSBool js_GetLocalVariable(JSContext *cx, JSObject *obj, jsval id, jsval *vp); extern JSBool js_SetLocalVariable(JSContext *cx, JSObject *obj, jsval id, jsval *vp); #ifdef DUMP_CALL_TABLE # define JSOPTION_LOGCALL_TOSOURCE JS_BIT(15) extern JSHashTable *js_CallTable; extern size_t js_LogCallToSourceLimit; extern void js_DumpCallTable(JSContext *cx); #endif /* * Refresh and return fp->scopeChain. It may be stale if block scopes are * active but not yet reflected by objects in the scope chain. If a block * scope contains a with, eval, XML filtering predicate, or similar such * dynamically scoped construct, then compile-time block scope at fp->blocks * must reflect at runtime. */ extern JSObject * js_GetScopeChain(JSContext *cx, JSStackFrame *fp); /* * Compute the 'this' parameter for a call with nominal 'this' given by thisp * and arguments including argv[-1] (nominal 'this') and argv[-2] (callee). * Activation objects ("Call" objects not created with "new Call()", i.e., * "Call" objects that have private data) may not be referred to by 'this', * per ECMA-262, so js_ComputeThis censors them. */ extern JSObject * js_ComputeThis(JSContext *cx, JSObject *thisp, jsval *argv); /* * NB: js_Invoke requires that cx is currently running JS (i.e., that cx->fp * is non-null), and that the callee, |this| parameter, and actual arguments * are already pushed on the stack under cx->fp->sp. */ extern JS_FRIEND_API(JSBool) js_Invoke(JSContext *cx, uintN argc, uintN flags); /* * Consolidated js_Invoke flags simply rename certain JSFRAME_* flags, so that * we can share bits stored in JSStackFrame.flags and passed to: * * js_Invoke * js_InternalInvoke * js_ValueToFunction * js_ValueToFunctionObject * js_ValueToCallableObject * js_ReportIsNotFunction * * See jsfun.h for the latter four and flag renaming macros. */ #define JSINVOKE_CONSTRUCT JSFRAME_CONSTRUCTING #define JSINVOKE_INTERNAL JSFRAME_INTERNAL #define JSINVOKE_SKIP_CALLER JSFRAME_SKIP_CALLER #define JSINVOKE_ITERATOR JSFRAME_ITERATOR /* * Mask to isolate construct and iterator flags for use with jsfun.h functions. */ #define JSINVOKE_FUNFLAGS (JSINVOKE_CONSTRUCT | JSINVOKE_ITERATOR) /* * "Internal" calls may come from C or C++ code using a JSContext on which no * JS is running (!cx->fp), so they may need to push a dummy JSStackFrame. */ #define js_InternalCall(cx,obj,fval,argc,argv,rval) \ js_InternalInvoke(cx, obj, fval, 0, argc, argv, rval) #define js_InternalConstruct(cx,obj,fval,argc,argv,rval) \ js_InternalInvoke(cx, obj, fval, JSINVOKE_CONSTRUCT, argc, argv, rval) extern JSBool js_InternalInvoke(JSContext *cx, JSObject *obj, jsval fval, uintN flags, uintN argc, jsval *argv, jsval *rval); extern JSBool js_InternalGetOrSet(JSContext *cx, JSObject *obj, jsid id, jsval fval, JSAccessMode mode, uintN argc, jsval *argv, jsval *rval); extern JSBool js_Execute(JSContext *cx, JSObject *chain, JSScript *script, JSStackFrame *down, uintN flags, jsval *result); extern JSBool js_CheckRedeclaration(JSContext *cx, JSObject *obj, jsid id, uintN attrs, JSObject **objp, JSProperty **propp); extern JSBool js_StrictlyEqual(jsval lval, jsval rval); extern JSBool js_InvokeConstructor(JSContext *cx, jsval *vp, uintN argc); extern JSBool js_Interpret(JSContext *cx, jsbytecode *pc, jsval *result); JS_END_EXTERN_C #endif /* jsinterp_h___ */ pacparser-1.4.5/src/spidermonkey/js/src/jsiter.c000066400000000000000000001005221464010763600216570ustar00rootroot00000000000000/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- * vim: set ts=8 sw=4 et tw=78: * * ***** BEGIN LICENSE BLOCK ***** * Version: MPL 1.1/GPL 2.0/LGPL 2.1 * * The contents of this file are subject to the Mozilla Public License Version * 1.1 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * http://www.mozilla.org/MPL/ * * Software distributed under the License is distributed on an "AS IS" basis, * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License * for the specific language governing rights and limitations under the * License. * * The Original Code is Mozilla Communicator client code, released * March 31, 1998. * * The Initial Developer of the Original Code is * Netscape Communications Corporation. * Portions created by the Initial Developer are Copyright (C) 1998 * the Initial Developer. All Rights Reserved. * * Contributor(s): * * Alternatively, the contents of this file may be used under the terms of * either of the GNU General Public License Version 2 or later (the "GPL"), * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), * in which case the provisions of the GPL or the LGPL are applicable instead * of those above. If you wish to allow use of your version of this file only * under the terms of either the GPL or the LGPL, and not to allow others to * use your version of this file under the terms of the MPL, indicate your * decision by deleting the provisions above and replace them with the notice * and other provisions required by the GPL or the LGPL. If you do not delete * the provisions above, a recipient may use your version of this file under * the terms of any one of the MPL, the GPL or the LGPL. * * ***** END LICENSE BLOCK ***** */ /* * JavaScript iterators. */ #include "jsstddef.h" #include /* for memcpy */ #include "jstypes.h" #include "jsutil.h" #include "jsarena.h" #include "jsapi.h" #include "jsarray.h" #include "jsatom.h" #include "jsbool.h" #include "jscntxt.h" #include "jsconfig.h" #include "jsexn.h" #include "jsfun.h" #include "jsgc.h" #include "jsinterp.h" #include "jsiter.h" #include "jslock.h" #include "jsnum.h" #include "jsobj.h" #include "jsopcode.h" #include "jsscope.h" #include "jsscript.h" #if JS_HAS_XML_SUPPORT #include "jsxml.h" #endif extern const char js_throw_str[]; /* from jsscan.h */ #define JSSLOT_ITER_STATE (JSSLOT_PRIVATE) #define JSSLOT_ITER_FLAGS (JSSLOT_PRIVATE + 1) #if JSSLOT_ITER_FLAGS >= JS_INITIAL_NSLOTS #error JS_INITIAL_NSLOTS must be greater than JSSLOT_ITER_FLAGS. #endif /* * Shared code to close iterator's state either through an explicit call or * when GC detects that the iterator is no longer reachable. */ void js_CloseIteratorState(JSContext *cx, JSObject *iterobj) { jsval *slots; jsval state, parent; JSObject *iterable; JS_ASSERT(JS_InstanceOf(cx, iterobj, &js_IteratorClass, NULL)); slots = iterobj->slots; /* Avoid double work if js_CloseNativeIterator was called on obj. */ state = slots[JSSLOT_ITER_STATE]; if (JSVAL_IS_NULL(state)) return; /* Protect against failure to fully initialize obj. */ parent = slots[JSSLOT_PARENT]; if (!JSVAL_IS_PRIMITIVE(parent)) { iterable = JSVAL_TO_OBJECT(parent); #if JS_HAS_XML_SUPPORT if ((JSVAL_TO_INT(slots[JSSLOT_ITER_FLAGS]) & JSITER_FOREACH) && OBJECT_IS_XML(cx, iterable)) { ((JSXMLObjectOps *) iterable->map->ops)-> enumerateValues(cx, iterable, JSENUMERATE_DESTROY, &state, NULL, NULL); } else #endif OBJ_ENUMERATE(cx, iterable, JSENUMERATE_DESTROY, &state, NULL); } slots[JSSLOT_ITER_STATE] = JSVAL_NULL; } JSClass js_IteratorClass = { "Iterator", JSCLASS_HAS_RESERVED_SLOTS(2) | /* slots for state and flags */ JSCLASS_HAS_CACHED_PROTO(JSProto_Iterator), JS_PropertyStub, JS_PropertyStub, JS_PropertyStub, JS_PropertyStub, JS_EnumerateStub, JS_ResolveStub, JS_ConvertStub, JS_FinalizeStub, JSCLASS_NO_OPTIONAL_MEMBERS }; static JSBool InitNativeIterator(JSContext *cx, JSObject *iterobj, JSObject *obj, uintN flags) { jsval state; JSBool ok; JS_ASSERT(JSVAL_TO_PRIVATE(iterobj->slots[JSSLOT_CLASS]) == &js_IteratorClass); /* Initialize iterobj in case of enumerate hook failure. */ iterobj->slots[JSSLOT_PARENT] = OBJECT_TO_JSVAL(obj); iterobj->slots[JSSLOT_ITER_STATE] = JSVAL_NULL; iterobj->slots[JSSLOT_ITER_FLAGS] = INT_TO_JSVAL(flags); if (!js_RegisterCloseableIterator(cx, iterobj)) return JS_FALSE; if (!obj) return JS_TRUE; ok = #if JS_HAS_XML_SUPPORT ((flags & JSITER_FOREACH) && OBJECT_IS_XML(cx, obj)) ? ((JSXMLObjectOps *) obj->map->ops)-> enumerateValues(cx, obj, JSENUMERATE_INIT, &state, NULL, NULL) : #endif OBJ_ENUMERATE(cx, obj, JSENUMERATE_INIT, &state, NULL); if (!ok) return JS_FALSE; iterobj->slots[JSSLOT_ITER_STATE] = state; if (flags & JSITER_ENUMERATE) { /* * The enumerating iterator needs the original object to suppress * enumeration of deleted or shadowed prototype properties. Since the * enumerator never escapes to scripts, we use the prototype slot to * store the original object. */ JS_ASSERT(obj != iterobj); iterobj->slots[JSSLOT_PROTO] = OBJECT_TO_JSVAL(obj); } return JS_TRUE; } static JSBool Iterator(JSContext *cx, JSObject *iterobj, uintN argc, jsval *argv, jsval *rval) { JSBool keyonly; uintN flags; JSObject *obj; keyonly = JS_FALSE; if (!js_ValueToBoolean(cx, argv[1], &keyonly)) return JS_FALSE; flags = keyonly ? 0 : JSITER_FOREACH; if (cx->fp->flags & JSFRAME_CONSTRUCTING) { /* XXX work around old valueOf call hidden beneath js_ValueToObject */ if (!JSVAL_IS_PRIMITIVE(argv[0])) { obj = JSVAL_TO_OBJECT(argv[0]); } else { obj = js_ValueToNonNullObject(cx, argv[0]); if (!obj) return JS_FALSE; argv[0] = OBJECT_TO_JSVAL(obj); } return InitNativeIterator(cx, iterobj, obj, flags); } *rval = argv[0]; return js_ValueToIterator(cx, flags, rval); } static JSBool NewKeyValuePair(JSContext *cx, jsid key, jsval val, jsval *rval) { jsval vec[2]; JSTempValueRooter tvr; JSObject *aobj; vec[0] = ID_TO_VALUE(key); vec[1] = val; JS_PUSH_TEMP_ROOT(cx, 2, vec, &tvr); aobj = js_NewArrayObject(cx, 2, vec); *rval = OBJECT_TO_JSVAL(aobj); JS_POP_TEMP_ROOT(cx, &tvr); return aobj != NULL; } static JSBool IteratorNextImpl(JSContext *cx, JSObject *obj, jsval *rval) { JSObject *iterable; jsval state; uintN flags; JSBool foreach, ok; jsid id; JS_ASSERT(OBJ_GET_CLASS(cx, obj) == &js_IteratorClass); iterable = OBJ_GET_PARENT(cx, obj); JS_ASSERT(iterable); state = OBJ_GET_SLOT(cx, obj, JSSLOT_ITER_STATE); if (JSVAL_IS_NULL(state)) goto stop; flags = JSVAL_TO_INT(OBJ_GET_SLOT(cx, obj, JSSLOT_ITER_FLAGS)); JS_ASSERT(!(flags & JSITER_ENUMERATE)); foreach = (flags & JSITER_FOREACH) != 0; ok = #if JS_HAS_XML_SUPPORT (foreach && OBJECT_IS_XML(cx, iterable)) ? ((JSXMLObjectOps *) iterable->map->ops)-> enumerateValues(cx, iterable, JSENUMERATE_NEXT, &state, &id, rval) : #endif OBJ_ENUMERATE(cx, iterable, JSENUMERATE_NEXT, &state, &id); if (!ok) return JS_FALSE; OBJ_SET_SLOT(cx, obj, JSSLOT_ITER_STATE, state); if (JSVAL_IS_NULL(state)) goto stop; if (foreach) { #if JS_HAS_XML_SUPPORT if (!OBJECT_IS_XML(cx, iterable) && !OBJ_GET_PROPERTY(cx, iterable, id, rval)) { return JS_FALSE; } #endif if (!NewKeyValuePair(cx, id, *rval, rval)) return JS_FALSE; } else { *rval = ID_TO_VALUE(id); } return JS_TRUE; stop: JS_ASSERT(OBJ_GET_SLOT(cx, obj, JSSLOT_ITER_STATE) == JSVAL_NULL); *rval = JSVAL_HOLE; return JS_TRUE; } static JSBool js_ThrowStopIteration(JSContext *cx, JSObject *obj) { jsval v; JS_ASSERT(!JS_IsExceptionPending(cx)); if (js_FindClassObject(cx, NULL, INT_TO_JSID(JSProto_StopIteration), &v)) JS_SetPendingException(cx, v); return JS_FALSE; } static JSBool iterator_next(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) { if (!JS_InstanceOf(cx, obj, &js_IteratorClass, argv)) return JS_FALSE; if (!IteratorNextImpl(cx, obj, rval)) return JS_FALSE; if (*rval == JSVAL_HOLE) { *rval = JSVAL_NULL; js_ThrowStopIteration(cx, obj); return JS_FALSE; } return JS_TRUE; } static JSBool iterator_self(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) { *rval = OBJECT_TO_JSVAL(obj); return JS_TRUE; } static JSFunctionSpec iterator_methods[] = { {js_iterator_str, iterator_self, 0,JSPROP_READONLY|JSPROP_PERMANENT,0}, {js_next_str, iterator_next, 0,JSPROP_READONLY|JSPROP_PERMANENT,0}, {0,0,0,0,0} }; uintN js_GetNativeIteratorFlags(JSContext *cx, JSObject *iterobj) { if (OBJ_GET_CLASS(cx, iterobj) != &js_IteratorClass) return 0; return JSVAL_TO_INT(OBJ_GET_SLOT(cx, iterobj, JSSLOT_ITER_FLAGS)); } void js_CloseNativeIterator(JSContext *cx, JSObject *iterobj) { uintN flags; /* * If this iterator is not an instance of the native default iterator * class, leave it to be GC'ed. */ if (!JS_InstanceOf(cx, iterobj, &js_IteratorClass, NULL)) return; /* * If this iterator was not created by js_ValueToIterator called from the * for-in loop code in js_Interpret, leave it to be GC'ed. */ flags = JSVAL_TO_INT(OBJ_GET_SLOT(cx, iterobj, JSSLOT_ITER_FLAGS)); if (!(flags & JSITER_ENUMERATE)) return; js_CloseIteratorState(cx, iterobj); } /* * Call ToObject(v).__iterator__(keyonly) if ToObject(v).__iterator__ exists. * Otherwise construct the defualt iterator. */ JSBool js_ValueToIterator(JSContext *cx, uintN flags, jsval *vp) { JSObject *obj; JSTempValueRooter tvr; const JSAtom *atom; JSBool ok; JSObject *iterobj; jsval arg; JSString *str; JS_ASSERT(!(flags & ~(JSITER_ENUMERATE | JSITER_FOREACH | JSITER_KEYVALUE))); /* JSITER_KEYVALUE must always come with JSITER_FOREACH */ JS_ASSERT(!(flags & JSITER_KEYVALUE) || (flags & JSITER_FOREACH)); /* XXX work around old valueOf call hidden beneath js_ValueToObject */ if (!JSVAL_IS_PRIMITIVE(*vp)) { obj = JSVAL_TO_OBJECT(*vp); } else { /* * Enumerating over null and undefined gives an empty enumerator. * This is contrary to ECMA-262 9.9 ToObject, invoked from step 3 of * the first production in 12.6.4 and step 4 of the second production, * but it's "web JS" compatible. */ if ((flags & JSITER_ENUMERATE)) { if (!js_ValueToObject(cx, *vp, &obj)) return JS_FALSE; if (!obj) goto default_iter; } else { obj = js_ValueToNonNullObject(cx, *vp); if (!obj) return JS_FALSE; } } JS_ASSERT(obj); JS_PUSH_TEMP_ROOT_OBJECT(cx, obj, &tvr); atom = cx->runtime->atomState.iteratorAtom; #if JS_HAS_XML_SUPPORT if (OBJECT_IS_XML(cx, obj)) { if (!js_GetXMLFunction(cx, obj, ATOM_TO_JSID(atom), vp)) goto bad; } else #endif { if (!OBJ_GET_PROPERTY(cx, obj, ATOM_TO_JSID(atom), vp)) goto bad; } if (JSVAL_IS_VOID(*vp)) { default_iter: /* * Fail over to the default enumerating native iterator. * * Create iterobj with a NULL parent to ensure that we use the correct * scope chain to lookup the iterator's constructor. Since we use the * parent slot to keep track of the iterable, we must fix it up after. */ iterobj = js_NewObject(cx, &js_IteratorClass, NULL, NULL); if (!iterobj) goto bad; /* Store iterobj in *vp to protect it from GC (callers must root vp). */ *vp = OBJECT_TO_JSVAL(iterobj); if (!InitNativeIterator(cx, iterobj, obj, flags)) goto bad; } else { arg = BOOLEAN_TO_JSVAL((flags & JSITER_FOREACH) == 0); if (!js_InternalInvoke(cx, obj, *vp, JSINVOKE_ITERATOR, 1, &arg, vp)) goto bad; if (JSVAL_IS_PRIMITIVE(*vp)) { str = js_DecompileValueGenerator(cx, JSDVG_SEARCH_STACK, *vp, NULL); if (str) { JS_ReportErrorNumberUC(cx, js_GetErrorMessage, NULL, JSMSG_BAD_ITERATOR_RETURN, JSSTRING_CHARS(str), JSSTRING_CHARS(ATOM_TO_STRING(atom))); } goto bad; } } ok = JS_TRUE; out: if (obj) JS_POP_TEMP_ROOT(cx, &tvr); return ok; bad: ok = JS_FALSE; goto out; } static JSBool CallEnumeratorNext(JSContext *cx, JSObject *iterobj, uintN flags, jsval *rval) { JSObject *obj, *origobj; jsval state; JSBool foreach; jsid id; JSObject *obj2; JSBool cond; JSClass *clasp; JSExtendedClass *xclasp; JSProperty *prop; JSString *str; JS_ASSERT(flags & JSITER_ENUMERATE); JS_ASSERT(JSVAL_TO_PRIVATE(iterobj->slots[JSSLOT_CLASS]) == &js_IteratorClass); obj = JSVAL_TO_OBJECT(iterobj->slots[JSSLOT_PARENT]); origobj = JSVAL_TO_OBJECT(iterobj->slots[JSSLOT_PROTO]); state = iterobj->slots[JSSLOT_ITER_STATE]; if (JSVAL_IS_NULL(state)) goto stop; foreach = (flags & JSITER_FOREACH) != 0; #if JS_HAS_XML_SUPPORT /* * Treat an XML object specially only when it starts the prototype chain. * Otherwise we need to do the usual deleted and shadowed property checks. */ if (obj == origobj && OBJECT_IS_XML(cx, obj)) { if (foreach) { JSXMLObjectOps *xmlops = (JSXMLObjectOps *) obj->map->ops; if (!xmlops->enumerateValues(cx, obj, JSENUMERATE_NEXT, &state, &id, rval)) { return JS_FALSE; } } else { if (!OBJ_ENUMERATE(cx, obj, JSENUMERATE_NEXT, &state, &id)) return JS_FALSE; } iterobj->slots[JSSLOT_ITER_STATE] = state; if (JSVAL_IS_NULL(state)) goto stop; } else #endif { restart: if (!OBJ_ENUMERATE(cx, obj, JSENUMERATE_NEXT, &state, &id)) return JS_TRUE; iterobj->slots[JSSLOT_ITER_STATE] = state; if (JSVAL_IS_NULL(state)) { #if JS_HAS_XML_SUPPORT if (OBJECT_IS_XML(cx, obj)) { /* * We just finished enumerating an XML obj that is present on * the prototype chain of a non-XML origobj. Stop further * prototype chain searches because XML objects don't * enumerate prototypes. */ JS_ASSERT(origobj != obj); JS_ASSERT(!OBJECT_IS_XML(cx, origobj)); } else #endif { obj = OBJ_GET_PROTO(cx, obj); if (obj) { iterobj->slots[JSSLOT_PARENT] = OBJECT_TO_JSVAL(obj); if (!OBJ_ENUMERATE(cx, obj, JSENUMERATE_INIT, &state, NULL)) return JS_FALSE; iterobj->slots[JSSLOT_ITER_STATE] = state; if (!JSVAL_IS_NULL(state)) goto restart; } } goto stop; } /* Skip properties not in obj when looking from origobj. */ if (!OBJ_LOOKUP_PROPERTY(cx, origobj, id, &obj2, &prop)) return JS_FALSE; if (!prop) goto restart; OBJ_DROP_PROPERTY(cx, obj2, prop); /* * If the id was found in a prototype object or an unrelated object * (specifically, not in an inner object for obj), skip it. This step * means that all OBJ_LOOKUP_PROPERTY implementations must return an * object further along on the prototype chain, or else possibly an * object returned by the JSExtendedClass.outerObject optional hook. */ if (obj != obj2) { cond = JS_FALSE; clasp = OBJ_GET_CLASS(cx, obj2); if (clasp->flags & JSCLASS_IS_EXTENDED) { xclasp = (JSExtendedClass *) clasp; cond = xclasp->outerObject && xclasp->outerObject(cx, obj2) == obj; } if (!cond) goto restart; } if (foreach) { /* Get property querying the original object. */ if (!OBJ_GET_PROPERTY(cx, origobj, id, rval)) return JS_FALSE; } } if (foreach) { if (flags & JSITER_KEYVALUE) { if (!NewKeyValuePair(cx, id, *rval, rval)) return JS_FALSE; } } else { /* Make rval a string for uniformity and compatibility. */ if (JSID_IS_ATOM(id)) { *rval = ATOM_KEY(JSID_TO_ATOM(id)); } #if JS_HAS_XML_SUPPORT else if (JSID_IS_OBJECT(id)) { str = js_ValueToString(cx, OBJECT_JSID_TO_JSVAL(id)); if (!str) return JS_FALSE; *rval = STRING_TO_JSVAL(str); } #endif else { str = js_NumberToString(cx, (jsdouble)JSID_TO_INT(id)); if (!str) return JS_FALSE; *rval = STRING_TO_JSVAL(str); } } return JS_TRUE; stop: JS_ASSERT(iterobj->slots[JSSLOT_ITER_STATE] == JSVAL_NULL); *rval = JSVAL_HOLE; return JS_TRUE; } JSBool js_CallIteratorNext(JSContext *cx, JSObject *iterobj, jsval *rval) { uintN flags; /* Fast path for native iterators */ if (OBJ_GET_CLASS(cx, iterobj) == &js_IteratorClass) { flags = JSVAL_TO_INT(OBJ_GET_SLOT(cx, iterobj, JSSLOT_ITER_FLAGS)); if (flags & JSITER_ENUMERATE) return CallEnumeratorNext(cx, iterobj, flags, rval); /* * Call next directly as all the methods of the native iterator are * read-only and permanent. */ if (!IteratorNextImpl(cx, iterobj, rval)) return JS_FALSE; } else { jsid id = ATOM_TO_JSID(cx->runtime->atomState.nextAtom); if (!JS_GetMethodById(cx, iterobj, id, &iterobj, rval)) return JS_FALSE; if (!js_InternalCall(cx, iterobj, *rval, 0, NULL, rval)) { /* Check for StopIteration. */ if (!cx->throwing || JSVAL_IS_PRIMITIVE(cx->exception) || OBJ_GET_CLASS(cx, JSVAL_TO_OBJECT(cx->exception)) != &js_StopIterationClass) { return JS_FALSE; } /* Inline JS_ClearPendingException(cx). */ cx->throwing = JS_FALSE; cx->exception = JSVAL_VOID; *rval = JSVAL_HOLE; return JS_TRUE; } } return JS_TRUE; } static JSBool stopiter_hasInstance(JSContext *cx, JSObject *obj, jsval v, JSBool *bp) { *bp = !JSVAL_IS_PRIMITIVE(v) && OBJ_GET_CLASS(cx, JSVAL_TO_OBJECT(v)) == &js_StopIterationClass; return JS_TRUE; } JSClass js_StopIterationClass = { js_StopIteration_str, JSCLASS_HAS_CACHED_PROTO(JSProto_StopIteration), JS_PropertyStub, JS_PropertyStub, JS_PropertyStub, JS_PropertyStub, JS_EnumerateStub, JS_ResolveStub, JS_ConvertStub, JS_FinalizeStub, NULL, NULL, NULL, NULL, NULL, stopiter_hasInstance, NULL, NULL }; #if JS_HAS_GENERATORS static void generator_finalize(JSContext *cx, JSObject *obj) { JSGenerator *gen; gen = (JSGenerator *) JS_GetPrivate(cx, obj); if (gen) { /* * gen can be open on shutdown when close hooks are ignored or when * the embedding cancels scheduled close hooks. */ JS_ASSERT(gen->state == JSGEN_NEWBORN || gen->state == JSGEN_CLOSED || gen->state == JSGEN_OPEN); JS_free(cx, gen); } } static uint32 generator_mark(JSContext *cx, JSObject *obj, void *arg) { JSGenerator *gen; gen = (JSGenerator *) JS_GetPrivate(cx, obj); if (gen) { /* * We must mark argv[-2], as js_MarkStackFrame will not. Note that * js_MarkStackFrame will mark thisp (argv[-1]) and actual arguments, * plus any missing formals and local GC roots. */ JS_ASSERT(!JSVAL_IS_PRIMITIVE(gen->frame.argv[-2])); GC_MARK(cx, JSVAL_TO_GCTHING(gen->frame.argv[-2]), "generator"); js_MarkStackFrame(cx, &gen->frame); } return 0; } JSClass js_GeneratorClass = { js_Generator_str, JSCLASS_HAS_PRIVATE | JSCLASS_IS_ANONYMOUS | JSCLASS_HAS_CACHED_PROTO(JSProto_Generator), JS_PropertyStub, JS_PropertyStub, JS_PropertyStub, JS_PropertyStub, JS_EnumerateStub, JS_ResolveStub, JS_ConvertStub, generator_finalize, NULL, NULL, NULL, NULL, NULL, NULL, generator_mark, NULL }; /* * Called from the JSOP_GENERATOR case in the interpreter, with fp referring * to the frame by which the generator function was activated. Create a new * JSGenerator object, which contains its own JSStackFrame that we populate * from *fp. We know that upon return, the JSOP_GENERATOR opcode will return * from the activation in fp, so we can steal away fp->callobj and fp->argsobj * if they are non-null. */ JSObject * js_NewGenerator(JSContext *cx, JSStackFrame *fp) { JSObject *obj; uintN argc, nargs, nvars, depth, nslots; JSGenerator *gen; jsval *newsp; /* After the following return, failing control flow must goto bad. */ obj = js_NewObject(cx, &js_GeneratorClass, NULL, NULL); if (!obj) return NULL; /* Load and compute stack slot counts. */ argc = fp->argc; nargs = JS_MAX(argc, fp->fun->nargs); nvars = fp->nvars; depth = fp->script->depth; nslots = 2 + nargs + nvars + 2 * depth; /* Allocate obj's private data struct. */ gen = (JSGenerator *) JS_malloc(cx, sizeof(JSGenerator) + (nslots - 1) * sizeof(jsval)); if (!gen) goto bad; gen->obj = obj; /* Steal away objects reflecting fp and point them at gen->frame. */ gen->frame.callobj = fp->callobj; if (fp->callobj) { JS_SetPrivate(cx, fp->callobj, &gen->frame); fp->callobj = NULL; } gen->frame.argsobj = fp->argsobj; if (fp->argsobj) { JS_SetPrivate(cx, fp->argsobj, &gen->frame); fp->argsobj = NULL; } /* These two references can be shared with fp until it goes away. */ gen->frame.varobj = fp->varobj; gen->frame.thisp = fp->thisp; /* Copy call-invariant script and function references. */ gen->frame.script = fp->script; gen->frame.fun = fp->fun; /* Use newsp to carve space out of gen->stack. */ newsp = gen->stack; gen->arena.next = NULL; gen->arena.base = (jsuword) newsp; gen->arena.limit = gen->arena.avail = (jsuword) (newsp + nslots); #define COPY_STACK_ARRAY(vec,cnt,num) \ JS_BEGIN_MACRO \ gen->frame.cnt = cnt; \ gen->frame.vec = newsp; \ newsp += (num); \ memcpy(gen->frame.vec, fp->vec, (num) * sizeof(jsval)); \ JS_END_MACRO /* Copy argv, rval, and vars. */ *newsp++ = fp->argv[-2]; *newsp++ = fp->argv[-1]; COPY_STACK_ARRAY(argv, argc, nargs); gen->frame.rval = fp->rval; COPY_STACK_ARRAY(vars, nvars, nvars); #undef COPY_STACK_ARRAY /* Initialize or copy virtual machine state. */ gen->frame.down = NULL; gen->frame.annotation = NULL; gen->frame.scopeChain = fp->scopeChain; gen->frame.pc = fp->pc; /* Allocate generating pc and operand stack space. */ gen->frame.spbase = gen->frame.sp = newsp + depth; /* Copy remaining state (XXX sharp* and xml* should be local vars). */ gen->frame.sharpDepth = 0; gen->frame.sharpArray = NULL; gen->frame.flags = fp->flags | JSFRAME_GENERATOR; gen->frame.dormantNext = NULL; gen->frame.xmlNamespace = NULL; gen->frame.blockChain = NULL; /* Note that gen is newborn. */ gen->state = JSGEN_NEWBORN; if (!JS_SetPrivate(cx, obj, gen)) { JS_free(cx, gen); goto bad; } /* * Register with GC to ensure that suspended finally blocks will be * executed. */ js_RegisterGenerator(cx, gen); return obj; bad: cx->weakRoots.newborn[GCX_OBJECT] = NULL; return NULL; } typedef enum JSGeneratorOp { JSGENOP_NEXT, JSGENOP_SEND, JSGENOP_THROW, JSGENOP_CLOSE } JSGeneratorOp; /* * Start newborn or restart yielding generator and perform the requested * operation inside its frame. */ static JSBool SendToGenerator(JSContext *cx, JSGeneratorOp op, JSObject *obj, JSGenerator *gen, jsval arg, jsval *rval) { JSStackFrame *fp; jsval junk; JSArena *arena; JSBool ok; JS_ASSERT(gen->state == JSGEN_NEWBORN || gen->state == JSGEN_OPEN); switch (op) { case JSGENOP_NEXT: case JSGENOP_SEND: if (gen->state == JSGEN_OPEN) { /* * Store the argument to send as the result of the yield * expression. */ gen->frame.sp[-1] = arg; } gen->state = JSGEN_RUNNING; break; case JSGENOP_THROW: JS_SetPendingException(cx, arg); gen->state = JSGEN_RUNNING; break; default: JS_ASSERT(op == JSGENOP_CLOSE); JS_SetPendingException(cx, JSVAL_ARETURN); gen->state = JSGEN_CLOSING; break; } /* Extend the current stack pool with gen->arena. */ arena = cx->stackPool.current; JS_ASSERT(!arena->next); JS_ASSERT(!gen->arena.next); JS_ASSERT(cx->stackPool.current != &gen->arena); cx->stackPool.current = arena->next = &gen->arena; /* Push gen->frame around the interpreter activation. */ fp = cx->fp; cx->fp = &gen->frame; gen->frame.down = fp; ok = js_Interpret(cx, gen->frame.pc, &junk); cx->fp = fp; gen->frame.down = NULL; /* Retract the stack pool and sanitize gen->arena. */ JS_ASSERT(!gen->arena.next); JS_ASSERT(arena->next == &gen->arena); JS_ASSERT(cx->stackPool.current == &gen->arena); cx->stackPool.current = arena; arena->next = NULL; if (gen->frame.flags & JSFRAME_YIELDING) { /* Yield cannot fail, throw or be called on closing. */ JS_ASSERT(ok); JS_ASSERT(!cx->throwing); JS_ASSERT(gen->state == JSGEN_RUNNING); JS_ASSERT(op != JSGENOP_CLOSE); gen->frame.flags &= ~JSFRAME_YIELDING; gen->state = JSGEN_OPEN; *rval = gen->frame.rval; return JS_TRUE; } gen->state = JSGEN_CLOSED; if (ok) { /* Returned, explicitly or by falling off the end. */ if (op == JSGENOP_CLOSE) return JS_TRUE; return js_ThrowStopIteration(cx, obj); } /* * An error, silent termination by branch callback or an exception. * Propagate the condition to the caller. */ return JS_FALSE; } /* * Execute gen's close hook after the GC detects that the object has become * unreachable. */ JSBool js_CloseGeneratorObject(JSContext *cx, JSGenerator *gen) { /* We pass null as rval since SendToGenerator never uses it with CLOSE. */ return SendToGenerator(cx, JSGENOP_CLOSE, gen->obj, gen, JSVAL_VOID, NULL); } /* * Common subroutine of generator_(next|send|throw|close) methods. */ static JSBool generator_op(JSContext *cx, JSGeneratorOp op, JSObject *obj, uintN argc, jsval *argv, jsval *rval) { JSGenerator *gen; JSString *str; jsval arg; if (!JS_InstanceOf(cx, obj, &js_GeneratorClass, argv)) return JS_FALSE; gen = (JSGenerator *) JS_GetPrivate(cx, obj); if (gen == NULL) { /* This happens when obj is the generator prototype. See bug 352885. */ goto closed_generator; } switch (gen->state) { case JSGEN_NEWBORN: switch (op) { case JSGENOP_NEXT: case JSGENOP_THROW: break; case JSGENOP_SEND: if (!JSVAL_IS_VOID(argv[0])) { str = js_DecompileValueGenerator(cx, JSDVG_SEARCH_STACK, argv[0], NULL); if (str) { JS_ReportErrorNumberUC(cx, js_GetErrorMessage, NULL, JSMSG_BAD_GENERATOR_SEND, JSSTRING_CHARS(str)); } return JS_FALSE; } break; default: JS_ASSERT(op == JSGENOP_CLOSE); gen->state = JSGEN_CLOSED; return JS_TRUE; } break; case JSGEN_OPEN: break; case JSGEN_RUNNING: case JSGEN_CLOSING: str = js_DecompileValueGenerator(cx, JSDVG_SEARCH_STACK, argv[-1], JS_GetFunctionId(gen->frame.fun)); if (str) { JS_ReportErrorNumberUC(cx, js_GetErrorMessage, NULL, JSMSG_NESTING_GENERATOR, JSSTRING_CHARS(str)); } return JS_FALSE; default: JS_ASSERT(gen->state == JSGEN_CLOSED); closed_generator: switch (op) { case JSGENOP_NEXT: case JSGENOP_SEND: return js_ThrowStopIteration(cx, obj); case JSGENOP_THROW: JS_SetPendingException(cx, argv[0]); return JS_FALSE; default: JS_ASSERT(op == JSGENOP_CLOSE); return JS_TRUE; } } arg = (op == JSGENOP_SEND || op == JSGENOP_THROW) ? argv[0] : JSVAL_VOID; if (!SendToGenerator(cx, op, obj, gen, arg, rval)) return JS_FALSE; return JS_TRUE; } static JSBool generator_send(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) { return generator_op(cx, JSGENOP_SEND, obj, argc, argv, rval); } static JSBool generator_next(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) { return generator_op(cx, JSGENOP_NEXT, obj, argc, argv, rval); } static JSBool generator_throw(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) { return generator_op(cx, JSGENOP_THROW, obj, argc, argv, rval); } static JSBool generator_close(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) { return generator_op(cx, JSGENOP_CLOSE, obj, argc, argv, rval); } static JSFunctionSpec generator_methods[] = { {js_iterator_str, iterator_self, 0,JSPROP_READONLY|JSPROP_PERMANENT,0}, {js_next_str, generator_next, 0,JSPROP_READONLY|JSPROP_PERMANENT,0}, {js_send_str, generator_send, 1,JSPROP_READONLY|JSPROP_PERMANENT,0}, {js_throw_str, generator_throw, 1,JSPROP_READONLY|JSPROP_PERMANENT,0}, {js_close_str, generator_close, 0,JSPROP_READONLY|JSPROP_PERMANENT,0}, {0,0,0,0,0} }; #endif /* JS_HAS_GENERATORS */ JSObject * js_InitIteratorClasses(JSContext *cx, JSObject *obj) { JSObject *proto, *stop; /* Idempotency required: we initialize several things, possibly lazily. */ if (!js_GetClassObject(cx, obj, JSProto_StopIteration, &stop)) return NULL; if (stop) return stop; proto = JS_InitClass(cx, obj, NULL, &js_IteratorClass, Iterator, 2, NULL, iterator_methods, NULL, NULL); if (!proto) return NULL; proto->slots[JSSLOT_ITER_STATE] = JSVAL_NULL; #if JS_HAS_GENERATORS /* Initialize the generator internals if configured. */ if (!JS_InitClass(cx, obj, NULL, &js_GeneratorClass, NULL, 0, NULL, generator_methods, NULL, NULL)) { return NULL; } #endif return JS_InitClass(cx, obj, NULL, &js_StopIterationClass, NULL, 0, NULL, NULL, NULL, NULL); } pacparser-1.4.5/src/spidermonkey/js/src/jsiter.h000066400000000000000000000077421464010763600216760ustar00rootroot00000000000000/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- * vim: set ts=8 sw=4 et tw=78: * * ***** BEGIN LICENSE BLOCK ***** * Version: MPL 1.1/GPL 2.0/LGPL 2.1 * * The contents of this file are subject to the Mozilla Public License Version * 1.1 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * http://www.mozilla.org/MPL/ * * Software distributed under the License is distributed on an "AS IS" basis, * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License * for the specific language governing rights and limitations under the * License. * * The Original Code is Mozilla Communicator client code, released * March 31, 1998. * * The Initial Developer of the Original Code is * Netscape Communications Corporation. * Portions created by the Initial Developer are Copyright (C) 1998 * the Initial Developer. All Rights Reserved. * * Contributor(s): * * Alternatively, the contents of this file may be used under the terms of * either of the GNU General Public License Version 2 or later (the "GPL"), * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), * in which case the provisions of the GPL or the LGPL are applicable instead * of those above. If you wish to allow use of your version of this file only * under the terms of either the GPL or the LGPL, and not to allow others to * use your version of this file under the terms of the MPL, indicate your * decision by deleting the provisions above and replace them with the notice * and other provisions required by the GPL or the LGPL. If you do not delete * the provisions above, a recipient may use your version of this file under * the terms of any one of the MPL, the GPL or the LGPL. * * ***** END LICENSE BLOCK ***** */ #ifndef jsiter_h___ #define jsiter_h___ /* * JavaScript iterators. */ #include "jsprvtd.h" #include "jspubtd.h" #define JSITER_ENUMERATE 0x1 /* for-in compatible hidden default iterator */ #define JSITER_FOREACH 0x2 /* return [key, value] pair rather than key */ #define JSITER_KEYVALUE 0x4 /* destructuring for-in wants [key, value] */ extern void js_CloseNativeIterator(JSContext *cx, JSObject *iterobj); extern void js_CloseIteratorState(JSContext *cx, JSObject *iterobj); /* * Convert the value stored in *vp to its iteration object. The flags should * contain JSITER_ENUMERATE if js_ValueToIterator is called when enumerating * for-in semantics are required, and when the caller can guarantee that the * iterator will never be exposed to scripts. */ extern JSBool js_ValueToIterator(JSContext *cx, uintN flags, jsval *vp); /* * Given iterobj, call iterobj.next(). If the iterator stopped, set *rval to * JSVAL_HOLE. Otherwise set it to the result of the next call. */ extern JSBool js_CallIteratorNext(JSContext *cx, JSObject *iterobj, jsval *rval); #if JS_HAS_GENERATORS /* * Generator state codes. */ typedef enum JSGeneratorState { JSGEN_NEWBORN, /* not yet started */ JSGEN_OPEN, /* started by a .next() or .send(undefined) call */ JSGEN_RUNNING, /* currently executing via .next(), etc., call */ JSGEN_CLOSING, /* close method is doing asynchronous return */ JSGEN_CLOSED /* closed, cannot be started or closed again */ } JSGeneratorState; struct JSGenerator { JSGenerator *next; JSObject *obj; JSGeneratorState state; JSStackFrame frame; JSArena arena; jsval stack[1]; }; #define FRAME_TO_GENERATOR(fp) \ ((JSGenerator *) ((uint8 *)(fp) - offsetof(JSGenerator, frame))) extern JSObject * js_NewGenerator(JSContext *cx, JSStackFrame *fp); extern JSBool js_CloseGeneratorObject(JSContext *cx, JSGenerator *gen); #endif extern JSClass js_GeneratorClass; extern JSClass js_IteratorClass; extern JSClass js_StopIterationClass; extern JSObject * js_InitIteratorClasses(JSContext *cx, JSObject *obj); #endif /* jsiter_h___ */ pacparser-1.4.5/src/spidermonkey/js/src/jskeyword.tbl000066400000000000000000000152471464010763600227500ustar00rootroot00000000000000/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- * vim: set sw=4 ts=8 et tw=80: * * ***** BEGIN LICENSE BLOCK ***** * Version: MPL 1.1/GPL 2.0/LGPL 2.1 * * The contents of this file are subject to the Mozilla Public License Version * 1.1 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * http://www.mozilla.org/MPL/ * * Software distributed under the License is distributed on an "AS IS" basis, * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License * for the specific language governing rights and limitations under the * License. * * The Original Code is Mozilla Communicator client code, released * March 31, 1998. * * The Initial Developer of the Original Code is * Netscape Communications Corporation. * Portions created by the Initial Developer are Copyright (C) 1998 * the Initial Developer. All Rights Reserved. * * Contributor(s): * * Alternatively, the contents of this file may be used under the terms of * either of the GNU General Public License Version 2 or later (the "GPL"), * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), * in which case the provisions of the GPL or the LGPL are applicable instead * of those above. If you wish to allow use of your version of this file only * under the terms of either the GPL or the LGPL, and not to allow others to * use your version of this file under the terms of the MPL, indicate your * decision by deleting the provisions above and replace them with the notice * and other provisions required by the GPL or the LGPL. If you do not delete * the provisions above, a recipient may use your version of this file under * the terms of any one of the MPL, the GPL or the LGPL. * * ***** END LICENSE BLOCK ***** */ JS_KEYWORD(break, TOK_BREAK, JSOP_NOP, JSVERSION_DEFAULT) JS_KEYWORD(case, TOK_CASE, JSOP_NOP, JSVERSION_DEFAULT) JS_KEYWORD(continue, TOK_CONTINUE, JSOP_NOP, JSVERSION_DEFAULT) JS_KEYWORD(default, TOK_DEFAULT, JSOP_NOP, JSVERSION_DEFAULT) JS_KEYWORD(delete, TOK_DELETE, JSOP_NOP, JSVERSION_DEFAULT) JS_KEYWORD(do, TOK_DO, JSOP_NOP, JSVERSION_DEFAULT) JS_KEYWORD(else, TOK_ELSE, JSOP_NOP, JSVERSION_DEFAULT) JS_KEYWORD(export, TOK_EXPORT, JSOP_NOP, JSVERSION_DEFAULT) JS_KEYWORD(false, TOK_PRIMARY, JSOP_FALSE, JSVERSION_DEFAULT) JS_KEYWORD(for, TOK_FOR, JSOP_NOP, JSVERSION_DEFAULT) JS_KEYWORD(function, TOK_FUNCTION, JSOP_NOP, JSVERSION_DEFAULT) JS_KEYWORD(if, TOK_IF, JSOP_NOP, JSVERSION_DEFAULT) JS_KEYWORD(in, TOK_IN, JSOP_IN, JSVERSION_DEFAULT) JS_KEYWORD(new, TOK_NEW, JSOP_NEW, JSVERSION_DEFAULT) JS_KEYWORD(null, TOK_PRIMARY, JSOP_NULL, JSVERSION_DEFAULT) JS_KEYWORD(return, TOK_RETURN, JSOP_NOP, JSVERSION_DEFAULT) JS_KEYWORD(switch, TOK_SWITCH, JSOP_NOP, JSVERSION_DEFAULT) JS_KEYWORD(this, TOK_PRIMARY, JSOP_THIS, JSVERSION_DEFAULT) JS_KEYWORD(true, TOK_PRIMARY, JSOP_TRUE, JSVERSION_DEFAULT) JS_KEYWORD(typeof, TOK_UNARYOP, JSOP_TYPEOF, JSVERSION_DEFAULT) JS_KEYWORD(var, TOK_VAR, JSOP_DEFVAR, JSVERSION_DEFAULT) JS_KEYWORD(void, TOK_UNARYOP, JSOP_VOID, JSVERSION_DEFAULT) JS_KEYWORD(while, TOK_WHILE, JSOP_NOP, JSVERSION_DEFAULT) JS_KEYWORD(with, TOK_WITH, JSOP_NOP, JSVERSION_DEFAULT) #if JS_HAS_CONST JS_KEYWORD(const, TOK_VAR, JSOP_DEFCONST, JSVERSION_DEFAULT) #else JS_KEYWORD(const, TOK_RESERVED, JSOP_NOP, JSVERSION_DEFAULT) #endif JS_KEYWORD(try, TOK_TRY, JSOP_NOP, JSVERSION_DEFAULT) JS_KEYWORD(catch, TOK_CATCH, JSOP_NOP, JSVERSION_DEFAULT) JS_KEYWORD(finally, TOK_FINALLY, JSOP_NOP, JSVERSION_DEFAULT) JS_KEYWORD(throw, TOK_THROW, JSOP_NOP, JSVERSION_DEFAULT) JS_KEYWORD(instanceof, TOK_INSTANCEOF, JSOP_INSTANCEOF,JSVERSION_DEFAULT) #if JS_HAS_RESERVED_JAVA_KEYWORDS JS_KEYWORD(abstract, TOK_RESERVED, JSOP_NOP, JSVERSION_DEFAULT) JS_KEYWORD(boolean, TOK_RESERVED, JSOP_NOP, JSVERSION_DEFAULT) JS_KEYWORD(byte, TOK_RESERVED, JSOP_NOP, JSVERSION_DEFAULT) JS_KEYWORD(char, TOK_RESERVED, JSOP_NOP, JSVERSION_DEFAULT) JS_KEYWORD(class, TOK_RESERVED, JSOP_NOP, JSVERSION_DEFAULT) JS_KEYWORD(double, TOK_RESERVED, JSOP_NOP, JSVERSION_DEFAULT) JS_KEYWORD(extends, TOK_RESERVED, JSOP_NOP, JSVERSION_DEFAULT) JS_KEYWORD(final, TOK_RESERVED, JSOP_NOP, JSVERSION_DEFAULT) JS_KEYWORD(float, TOK_RESERVED, JSOP_NOP, JSVERSION_DEFAULT) JS_KEYWORD(goto, TOK_RESERVED, JSOP_NOP, JSVERSION_DEFAULT) JS_KEYWORD(implements, TOK_RESERVED, JSOP_NOP, JSVERSION_DEFAULT) JS_KEYWORD(import, TOK_IMPORT, JSOP_NOP, JSVERSION_DEFAULT) JS_KEYWORD(int, TOK_RESERVED, JSOP_NOP, JSVERSION_DEFAULT) JS_KEYWORD(interface, TOK_RESERVED, JSOP_NOP, JSVERSION_DEFAULT) JS_KEYWORD(long, TOK_RESERVED, JSOP_NOP, JSVERSION_DEFAULT) JS_KEYWORD(native, TOK_RESERVED, JSOP_NOP, JSVERSION_DEFAULT) JS_KEYWORD(package, TOK_RESERVED, JSOP_NOP, JSVERSION_DEFAULT) JS_KEYWORD(private, TOK_RESERVED, JSOP_NOP, JSVERSION_DEFAULT) JS_KEYWORD(protected, TOK_RESERVED, JSOP_NOP, JSVERSION_DEFAULT) JS_KEYWORD(public, TOK_RESERVED, JSOP_NOP, JSVERSION_DEFAULT) JS_KEYWORD(short, TOK_RESERVED, JSOP_NOP, JSVERSION_DEFAULT) JS_KEYWORD(static, TOK_RESERVED, JSOP_NOP, JSVERSION_DEFAULT) JS_KEYWORD(super, TOK_RESERVED, JSOP_NOP, JSVERSION_DEFAULT) JS_KEYWORD(synchronized,TOK_RESERVED, JSOP_NOP, JSVERSION_DEFAULT) JS_KEYWORD(throws, TOK_RESERVED, JSOP_NOP, JSVERSION_DEFAULT) JS_KEYWORD(transient, TOK_RESERVED, JSOP_NOP, JSVERSION_DEFAULT) JS_KEYWORD(volatile, TOK_RESERVED, JSOP_NOP, JSVERSION_DEFAULT) #endif #if JS_HAS_RESERVED_ECMA_KEYWORDS JS_KEYWORD(enum, TOK_RESERVED, JSOP_NOP, JSVERSION_DEFAULT) #endif #if JS_HAS_DEBUGGER_KEYWORD JS_KEYWORD(debugger, TOK_DEBUGGER, JSOP_NOP, JSVERSION_DEFAULT) #elif JS_HAS_RESERVED_ECMA_KEYWORDS JS_KEYWORD(debugger, TOK_RESERVED, JSOP_NOP, JSVERSION_DEFAULT) #endif #if JS_HAS_GENERATORS JS_KEYWORD(yield, TOK_YIELD, JSOP_NOP, JSVERSION_1_7) #endif #if JS_HAS_BLOCK_SCOPE JS_KEYWORD(let, TOK_LET, JSOP_NOP, JSVERSION_1_7) #endif pacparser-1.4.5/src/spidermonkey/js/src/jskwgen.c000066400000000000000000000321501464010763600220300ustar00rootroot00000000000000/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- * vim: set sw=4 ts=8 et tw=80: * * ***** BEGIN LICENSE BLOCK ***** * Version: MPL 1.1/GPL 2.0/LGPL 2.1 * * The contents of this file are subject to the Mozilla Public License Version * 1.1 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * http://www.mozilla.org/MPL/ * * Software distributed under the License is distributed on an "AS IS" basis, * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License * for the specific language governing rights and limitations under the * License. * * The Original Code is String Switch Generator for JavaScript Keywords, * released 2005-12-09. * * The Initial Developer of the Original Code is * Igor Bukanov. * Portions created by the Initial Developer are Copyright (C) 2005-2006 * the Initial Developer. All Rights Reserved. * * Contributor(s): * * Alternatively, the contents of this file may be used under the terms of * either of the GNU General Public License Version 2 or later (the "GPL"), * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), * in which case the provisions of the GPL or the LGPL are applicable instead * of those above. If you wish to allow use of your version of this file only * under the terms of either the GPL or the LGPL, and not to allow others to * use your version of this file under the terms of the MPL, indicate your * decision by deleting the provisions above and replace them with the notice * and other provisions required by the GPL or the LGPL. If you do not delete * the provisions above, a recipient may use your version of this file under * the terms of any one of the MPL, the GPL or the LGPL. * * ***** END LICENSE BLOCK ***** */ #include "jsstddef.h" #include #include #include #include #include #include #include "jsconfig.h" const char * const keyword_list[] = { #define JS_KEYWORD(keyword, type, op, version) #keyword, #include "jskeyword.tbl" #undef JS_KEYWORD }; struct gen_opt { FILE *output; /* output file for generated source */ unsigned use_if_threshold; /* max number of choices to generate "if" selector instead of "switch" */ unsigned char_tail_test_threshold; /* max number of unprocessed columns to use inlined char compare for remaining chars and not generic string compare code */ unsigned indent_level; /* current source identation level */ }; static unsigned column_to_compare; static int length_comparator(const void *a, const void *b) { const char *str1 = keyword_list[*(unsigned *)a]; const char *str2 = keyword_list[*(unsigned *)b]; return (int)strlen(str1) - (int)strlen(str2); } static int column_comparator(const void *a, const void *b) { const char *str1 = keyword_list[*(unsigned *)a]; const char *str2 = keyword_list[*(unsigned *)b]; return (int)str1[column_to_compare] - (int)str2[column_to_compare]; } static unsigned count_different_lengths(unsigned indexes[], unsigned nelem) { unsigned nlength, current_length, i, l; current_length = 0; nlength = 0; for (i = 0; i != nelem; ++i) { l = (unsigned)strlen(keyword_list[indexes[i]]); assert(l != 0); if (current_length != l) { ++nlength; current_length = l; } } return nlength; } static void find_char_span_and_count(unsigned indexes[], unsigned nelem, unsigned column, unsigned *span_result, unsigned *count_result) { unsigned i, count; unsigned char c, prev, minc, maxc; assert(nelem != 0); minc = maxc = prev = (unsigned char)keyword_list[indexes[0]][column]; count = 1; for (i = 1; i != nelem; ++i) { c = (unsigned char)keyword_list[indexes[i]][column]; if (prev != c) { prev = c; ++count; if (minc > c) { minc = c; } else if (maxc < c) { maxc = c; } } } *span_result = maxc - minc + 1; *count_result = count; } static unsigned find_optimal_switch_column(struct gen_opt *opt, unsigned indexes[], unsigned nelem, unsigned columns[], unsigned unprocessed_columns, int *use_if_result) { unsigned i; unsigned span, min_span, min_span_index; unsigned nchar, min_nchar, min_nchar_index; assert(unprocessed_columns != 0); i = 0; min_nchar = min_span = (unsigned)-1; min_nchar_index = min_span_index = 0; do { column_to_compare = columns[i]; qsort(indexes, nelem, sizeof(indexes[0]), column_comparator); find_char_span_and_count(indexes, nelem, column_to_compare, &span, &nchar); assert(span != 0); if (span == 1) { assert(nchar == 1); *use_if_result = 1; return 1; } assert(nchar != 1); if (min_span > span) { min_span = span; min_span_index = i; } if (min_nchar > nchar) { min_nchar = nchar; min_nchar_index = i; } } while (++i != unprocessed_columns); if (min_nchar <= opt->use_if_threshold) { *use_if_result = 1; i = min_nchar_index; } else { *use_if_result = 0; i = min_span_index; } /* * Restore order corresponding to i if it was destroyed by * subsequent sort. */ if (i != unprocessed_columns - 1) { column_to_compare = columns[i]; qsort(indexes, nelem, sizeof(indexes[0]), column_comparator); } return i; } static void p(struct gen_opt *opt, const char *format, ...) { va_list ap; va_start(ap, format); vfprintf(opt->output, format, ap); va_end(ap); } /* Size for '\xxx' where xxx is octal escape */ #define MIN_QUOTED_CHAR_BUFFER 7 static char * qchar(char c, char *quoted_buffer) { char *s; s = quoted_buffer; *s++ = '\''; switch (c) { case '\n': c = 'n'; goto one_char_escape; case '\r': c = 'r'; goto one_char_escape; case '\t': c = 't'; goto one_char_escape; case '\f': c = 't'; goto one_char_escape; case '\0': c = '0'; goto one_char_escape; case '\'': goto one_char_escape; one_char_escape: *s++ = '\\'; break; default: if (!isprint(c)) { *s++ = '\\'; *s++ = (char)('0' + (0x3 & (((unsigned char)c) >> 6))); *s++ = (char)('0' + (0x7 & (((unsigned char)c) >> 3))); c = (char)('0' + (0x7 & ((unsigned char)c))); } } *s++ = c; *s++ = '\''; *s = '\0'; assert(s + 1 <= quoted_buffer + MIN_QUOTED_CHAR_BUFFER); return quoted_buffer; } static void nl(struct gen_opt *opt) { putc('\n', opt->output); } static void indent(struct gen_opt *opt) { unsigned n = opt->indent_level; while (n != 0) { --n; fputs(" ", opt->output); } } static void line(struct gen_opt *opt, const char *format, ...) { va_list ap; indent(opt); va_start(ap, format); vfprintf(opt->output, format, ap); va_end(ap); nl(opt); } static void generate_letter_switch_r(struct gen_opt *opt, unsigned indexes[], unsigned nelem, unsigned columns[], unsigned unprocessed_columns) { char qbuf[MIN_QUOTED_CHAR_BUFFER]; assert(nelem != 0); if (nelem == 1) { unsigned kw_index = indexes[0]; const char *keyword = keyword_list[kw_index]; if (unprocessed_columns == 0) { line(opt, "JSKW_GOT_MATCH(%u) /* %s */", kw_index, keyword); } else if (unprocessed_columns > opt->char_tail_test_threshold) { line(opt, "JSKW_TEST_GUESS(%u) /* %s */", kw_index, keyword); } else { unsigned i, column; indent(opt); p(opt, "if ("); for (i = 0; i != unprocessed_columns; ++i) { column = columns[i]; qchar(keyword[column], qbuf); p(opt, "%sJSKW_AT(%u)==%s", (i == 0) ? "" : " && ", column, qbuf); } p(opt, ") {"); nl(opt); ++opt->indent_level; line(opt, "JSKW_GOT_MATCH(%u) /* %s */", kw_index, keyword); --opt->indent_level; line(opt, "}"); line(opt, "JSKW_NO_MATCH()"); } } else { unsigned optimal_column_index, optimal_column; unsigned i; int use_if; char current; assert(unprocessed_columns != 0); optimal_column_index = find_optimal_switch_column(opt, indexes, nelem, columns, unprocessed_columns, &use_if); optimal_column = columns[optimal_column_index]; columns[optimal_column_index] = columns[unprocessed_columns - 1]; if (!use_if) line(opt, "switch (JSKW_AT(%u)) {", optimal_column); current = keyword_list[indexes[0]][optimal_column]; for (i = 0; i != nelem;) { unsigned same_char_begin = i; char next = current; for (++i; i != nelem; ++i) { next = keyword_list[indexes[i]][optimal_column]; if (next != current) break; } qchar(current, qbuf); if (use_if) { line(opt, "if (JSKW_AT(%u) == %s) {", optimal_column, qbuf); } else { line(opt, " case %s:", qbuf); } ++opt->indent_level; generate_letter_switch_r(opt, indexes + same_char_begin, i - same_char_begin, columns, unprocessed_columns - 1); --opt->indent_level; if (use_if) { line(opt, "}"); } current = next; } if (!use_if) { line(opt, "}"); } columns[optimal_column_index] = optimal_column; line(opt, "JSKW_NO_MATCH()"); } } static void generate_letter_switch(struct gen_opt *opt, unsigned indexes[], unsigned nelem, unsigned current_length) { unsigned *columns; unsigned i; columns = malloc(sizeof(columns[0]) * current_length); if (!columns) { perror("malloc"); exit(EXIT_FAILURE); } for (i = 0; i != current_length; ++i) { columns[i] = i; } generate_letter_switch_r(opt, indexes, nelem, columns, current_length); free(columns); } static void generate_switch(struct gen_opt *opt) { unsigned *indexes; unsigned nlength; unsigned i, current; int use_if; unsigned nelem; nelem = sizeof(keyword_list)/sizeof(keyword_list[0]); line(opt, "/*"); line(opt, " * Generating switch for the list of %u entries:", nelem); for (i = 0; i != nelem; ++i) { line(opt, " * %s", keyword_list[i]); } line(opt, " */"); indexes = malloc(sizeof(indexes[0]) * nelem); if (!indexes) { perror("malloc"); exit(EXIT_FAILURE); } for (i = 0; i != nelem; ++i) indexes[i] = i; qsort(indexes, nelem, sizeof(indexes[i]), length_comparator); nlength = count_different_lengths(indexes, nelem); use_if = (nlength <= opt->use_if_threshold); if (!use_if) line(opt, "switch (JSKW_LENGTH()) {"); current = (unsigned)strlen(keyword_list[indexes[0]]); for (i = 0; i != nelem;) { unsigned same_length_begin = i; unsigned next = current; for (++i; i != nelem; ++i) { next = (unsigned)strlen(keyword_list[indexes[i]]); if (next != current) break; } if (use_if) { line(opt, "if (JSKW_LENGTH() == %u) {", current); } else { line(opt, " case %u:", current); } ++opt->indent_level; generate_letter_switch(opt, indexes + same_length_begin, i - same_length_begin, current); --opt->indent_level; if (use_if) { line(opt, "}"); } current = next; } if (!use_if) line(opt, "}"); line(opt, "JSKW_NO_MATCH()"); free(indexes); } int main(int argc, char **argv) { struct gen_opt opt; if (argc < 2) { opt.output = stdout; } else { opt.output = fopen(argv[1], "w"); if (!opt.output) { perror("fopen"); exit(EXIT_FAILURE); } } opt.indent_level = 1; opt.use_if_threshold = 3; opt.char_tail_test_threshold = 4; generate_switch(&opt); if (opt.output != stdout) { if (fclose(opt.output)) { perror("fclose"); exit(EXIT_FAILURE); } } return EXIT_SUCCESS; } pacparser-1.4.5/src/spidermonkey/js/src/jslibmath.h000066400000000000000000000155431464010763600223510ustar00rootroot00000000000000/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- * * ***** BEGIN LICENSE BLOCK ***** * Version: MPL 1.1/GPL 2.0/LGPL 2.1 * * The contents of this file are subject to the Mozilla Public License Version * 1.1 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * http://www.mozilla.org/MPL/ * * Software distributed under the License is distributed on an "AS IS" basis, * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License * for the specific language governing rights and limitations under the * License. * * The Original Code is Mozilla Communicator client code, released * March 31, 1998. * * The Initial Developer of the Original Code is * Netscape Communications Corporation. * Portions created by the Initial Developer are Copyright (C) 1998 * the Initial Developer. All Rights Reserved. * * Contributor(s): * IBM Corp. * * Alternatively, the contents of this file may be used under the terms of * either of the GNU General Public License Version 2 or later (the "GPL"), * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), * in which case the provisions of the GPL or the LGPL are applicable instead * of those above. If you wish to allow use of your version of this file only * under the terms of either the GPL or the LGPL, and not to allow others to * use your version of this file under the terms of the MPL, indicate your * decision by deleting the provisions above and replace them with the notice * and other provisions required by the GPL or the LGPL. If you do not delete * the provisions above, a recipient may use your version of this file under * the terms of any one of the MPL, the GPL or the LGPL. * * ***** END LICENSE BLOCK ***** */ /* * By default all math calls go to fdlibm. The defines for each platform * remap the math calls to native routines. */ #ifndef _LIBMATH_H #define _LIBMATH_H #include #include "jsconfig.h" /* * Define on which platforms to use fdlibm. Not used by default under * assumption that native math library works unless proved guilty. * Plus there can be problems with endian-ness and such in fdlibm itself. * * fdlibm compatibility notes: * - fdlibm broken on OSF1/alpha */ #ifndef JS_USE_FDLIBM_MATH #define JS_USE_FDLIBM_MATH 0 #endif #if !JS_USE_FDLIBM_MATH /* * Use system provided math routines. */ #define fd_acos acos #define fd_asin asin #define fd_atan atan #define fd_atan2 atan2 #define fd_ceil ceil /* The right copysign function is not always named the same thing. */ #if __GNUC__ >= 4 || (__GNUC__ == 3 && __GNUC_MINOR__ >= 4) #define fd_copysign __builtin_copysign #elif defined WINCE #define fd_copysign _copysign #elif defined _WIN32 #if _MSC_VER < 1400 /* Try to work around apparent _copysign bustage in VC6 and VC7. */ #define fd_copysign js_copysign extern double js_copysign(double, double); #else #define fd_copysign _copysign #endif #else #define fd_copysign copysign #endif #define fd_cos cos #define fd_exp exp #define fd_fabs fabs #define fd_floor floor #define fd_fmod fmod #define fd_log log #define fd_pow pow #define fd_sin sin #define fd_sqrt sqrt #define fd_tan tan #else /* * Use math routines in fdlibm. */ #undef __P #ifdef __STDC__ #define __P(p) p #else #define __P(p) () #endif #if (defined _WIN32 && !defined WINCE) || defined SUNOS4 #define fd_acos acos #define fd_asin asin #define fd_atan atan #define fd_cos cos #define fd_sin sin #define fd_tan tan #define fd_exp exp #define fd_log log #define fd_sqrt sqrt #define fd_ceil ceil #define fd_fabs fabs #define fd_floor floor #define fd_fmod fmod extern double fd_atan2 __P((double, double)); extern double fd_copysign __P((double, double)); extern double fd_pow __P((double, double)); #elif defined IRIX #define fd_acos acos #define fd_asin asin #define fd_atan atan #define fd_exp exp #define fd_log log #define fd_log10 log10 #define fd_sqrt sqrt #define fd_fabs fabs #define fd_floor floor #define fd_fmod fmod extern double fd_cos __P((double)); extern double fd_sin __P((double)); extern double fd_tan __P((double)); extern double fd_atan2 __P((double, double)); extern double fd_pow __P((double, double)); extern double fd_ceil __P((double)); extern double fd_copysign __P((double, double)); #elif defined SOLARIS #define fd_atan atan #define fd_cos cos #define fd_sin sin #define fd_tan tan #define fd_exp exp #define fd_sqrt sqrt #define fd_ceil ceil #define fd_fabs fabs #define fd_floor floor #define fd_fmod fmod extern double fd_acos __P((double)); extern double fd_asin __P((double)); extern double fd_log __P((double)); extern double fd_atan2 __P((double, double)); extern double fd_pow __P((double, double)); extern double fd_copysign __P((double, double)); #elif defined HPUX #define fd_cos cos #define fd_sin sin #define fd_exp exp #define fd_sqrt sqrt #define fd_fabs fabs #define fd_floor floor #define fd_fmod fmod extern double fd_ceil __P((double)); extern double fd_acos __P((double)); extern double fd_log __P((double)); extern double fd_atan2 __P((double, double)); extern double fd_tan __P((double)); extern double fd_pow __P((double, double)); extern double fd_asin __P((double)); extern double fd_atan __P((double)); extern double fd_copysign __P((double, double)); #elif defined(OSF1) #define fd_acos acos #define fd_asin asin #define fd_atan atan #define fd_copysign copysign #define fd_cos cos #define fd_exp exp #define fd_fabs fabs #define fd_fmod fmod #define fd_sin sin #define fd_sqrt sqrt #define fd_tan tan extern double fd_atan2 __P((double, double)); extern double fd_ceil __P((double)); extern double fd_floor __P((double)); extern double fd_log __P((double)); extern double fd_pow __P((double, double)); #elif defined(AIX) #define fd_acos acos #define fd_asin asin #define fd_atan2 atan2 #define fd_copysign copysign #define fd_cos cos #define fd_exp exp #define fd_fabs fabs #define fd_floor floor #define fd_fmod fmod #define fd_log log #define fd_sin sin #define fd_sqrt sqrt extern double fd_atan __P((double)); extern double fd_ceil __P((double)); extern double fd_pow __P((double,double)); extern double fd_tan __P((double)); #else /* other platform.. generic paranoid slow fdlibm */ extern double fd_acos __P((double)); extern double fd_asin __P((double)); extern double fd_atan __P((double)); extern double fd_cos __P((double)); extern double fd_sin __P((double)); extern double fd_tan __P((double)); extern double fd_exp __P((double)); extern double fd_log __P((double)); extern double fd_sqrt __P((double)); extern double fd_ceil __P((double)); extern double fd_fabs __P((double)); extern double fd_floor __P((double)); extern double fd_fmod __P((double, double)); extern double fd_atan2 __P((double, double)); extern double fd_pow __P((double, double)); extern double fd_copysign __P((double, double)); #endif #endif /* JS_USE_FDLIBM_MATH */ #endif /* _LIBMATH_H */ pacparser-1.4.5/src/spidermonkey/js/src/jslock.c000066400000000000000000001143671464010763600216600ustar00rootroot00000000000000/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- * * ***** BEGIN LICENSE BLOCK ***** * Version: MPL 1.1/GPL 2.0/LGPL 2.1 * * The contents of this file are subject to the Mozilla Public License Version * 1.1 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * http://www.mozilla.org/MPL/ * * Software distributed under the License is distributed on an "AS IS" basis, * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License * for the specific language governing rights and limitations under the * License. * * The Original Code is Mozilla Communicator client code, released * March 31, 1998. * * The Initial Developer of the Original Code is * Netscape Communications Corporation. * Portions created by the Initial Developer are Copyright (C) 1998 * the Initial Developer. All Rights Reserved. * * Contributor(s): * * Alternatively, the contents of this file may be used under the terms of * either of the GNU General Public License Version 2 or later (the "GPL"), * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), * in which case the provisions of the GPL or the LGPL are applicable instead * of those above. If you wish to allow use of your version of this file only * under the terms of either the GPL or the LGPL, and not to allow others to * use your version of this file under the terms of the MPL, indicate your * decision by deleting the provisions above and replace them with the notice * and other provisions required by the GPL or the LGPL. If you do not delete * the provisions above, a recipient may use your version of this file under * the terms of any one of the MPL, the GPL or the LGPL. * * ***** END LICENSE BLOCK ***** */ #ifdef JS_THREADSAFE /* * JS locking stubs. */ #include "jsstddef.h" #include #include "jspubtd.h" #include "jsutil.h" /* Added by JSIFY */ #include "jstypes.h" #include "jsbit.h" #include "jscntxt.h" #include "jsdtoa.h" #include "jsgc.h" #include "jslock.h" #include "jsscope.h" #include "jsstr.h" #define ReadWord(W) (W) #ifndef NSPR_LOCK #include static PRLock **global_locks; static uint32 global_lock_count = 1; static uint32 global_locks_log2 = 0; static uint32 global_locks_mask = 0; #define GLOBAL_LOCK_INDEX(id) (((uint32)(id) >> 2) & global_locks_mask) static void js_LockGlobal(void *id) { uint32 i = GLOBAL_LOCK_INDEX(id); PR_Lock(global_locks[i]); } static void js_UnlockGlobal(void *id) { uint32 i = GLOBAL_LOCK_INDEX(id); PR_Unlock(global_locks[i]); } /* Exclude Alpha NT. */ #if defined(_WIN32) && defined(_M_IX86) #pragma warning( disable : 4035 ) static JS_INLINE int js_CompareAndSwap(jsword *w, jsword ov, jsword nv) { __asm { mov eax, ov mov ecx, nv mov ebx, w lock cmpxchg [ebx], ecx sete al and eax, 1h } } #elif defined(__GNUC__) && defined(__i386__) /* Note: This fails on 386 cpus, cmpxchgl is a >= 486 instruction */ static JS_INLINE int js_CompareAndSwap(jsword *w, jsword ov, jsword nv) { unsigned int res; __asm__ __volatile__ ( "lock\n" "cmpxchgl %2, (%1)\n" "sete %%al\n" "andl $1, %%eax\n" : "=a" (res) : "r" (w), "r" (nv), "a" (ov) : "cc", "memory"); return (int)res; } #elif (defined(__USLC__) || defined(_SCO_DS)) && defined(i386) /* Note: This fails on 386 cpus, cmpxchgl is a >= 486 instruction */ asm int js_CompareAndSwap(jsword *w, jsword ov, jsword nv) { %ureg w, nv; movl ov,%eax lock cmpxchgl nv,(w) sete %al andl $1,%eax %ureg w; mem ov, nv; movl ov,%eax movl nv,%ecx lock cmpxchgl %ecx,(w) sete %al andl $1,%eax %ureg nv; movl ov,%eax movl w,%edx lock cmpxchgl nv,(%edx) sete %al andl $1,%eax %mem w, ov, nv; movl ov,%eax movl nv,%ecx movl w,%edx lock cmpxchgl %ecx,(%edx) sete %al andl $1,%eax } #pragma asm full_optimization js_CompareAndSwap #elif defined(SOLARIS) && defined(sparc) && defined(ULTRA_SPARC) static JS_INLINE int js_CompareAndSwap(jsword *w, jsword ov, jsword nv) { #if defined(__GNUC__) unsigned int res; JS_ASSERT(ov != nv); asm volatile ("\ stbar\n\ cas [%1],%2,%3\n\ cmp %2,%3\n\ be,a 1f\n\ mov 1,%0\n\ mov 0,%0\n\ 1:" : "=r" (res) : "r" (w), "r" (ov), "r" (nv)); return (int)res; #else /* !__GNUC__ */ extern int compare_and_swap(jsword*, jsword, jsword); JS_ASSERT(ov != nv); return compare_and_swap(w, ov, nv); #endif } #elif defined(AIX) #include static JS_INLINE int js_CompareAndSwap(jsword *w, jsword ov, jsword nv) { return !_check_lock((atomic_p)w, ov, nv); } #else #error "Define NSPR_LOCK if your platform lacks a compare-and-swap instruction." #endif /* arch-tests */ #endif /* !NSPR_LOCK */ void js_InitLock(JSThinLock *tl) { #ifdef NSPR_LOCK tl->owner = 0; tl->fat = (JSFatLock*)JS_NEW_LOCK(); #else memset(tl, 0, sizeof(JSThinLock)); #endif } void js_FinishLock(JSThinLock *tl) { #ifdef NSPR_LOCK tl->owner = 0xdeadbeef; if (tl->fat) JS_DESTROY_LOCK(((JSLock*)tl->fat)); #else JS_ASSERT(tl->owner == 0); JS_ASSERT(tl->fat == NULL); #endif } static void js_Dequeue(JSThinLock *); #ifdef DEBUG_SCOPE_COUNT #include #include "jsdhash.h" static FILE *logfp; static JSDHashTable logtbl; typedef struct logentry { JSDHashEntryStub stub; char op; const char *file; int line; } logentry; static void logit(JSScope *scope, char op, const char *file, int line) { logentry *entry; if (!logfp) { logfp = fopen("/tmp/scope.log", "w"); if (!logfp) return; setvbuf(logfp, NULL, _IONBF, 0); } fprintf(logfp, "%p %c %s %d\n", scope, op, file, line); if (!logtbl.entryStore && !JS_DHashTableInit(&logtbl, JS_DHashGetStubOps(), NULL, sizeof(logentry), 100)) { return; } entry = (logentry *) JS_DHashTableOperate(&logtbl, scope, JS_DHASH_ADD); if (!entry) return; entry->stub.key = scope; entry->op = op; entry->file = file; entry->line = line; } void js_unlog_scope(JSScope *scope) { if (!logtbl.entryStore) return; (void) JS_DHashTableOperate(&logtbl, scope, JS_DHASH_REMOVE); } # define LOGIT(scope,op) logit(scope, op, __FILE__, __LINE__) #else # define LOGIT(scope,op) /* nothing */ #endif /* DEBUG_SCOPE_COUNT */ /* * Return true if scope's ownercx, or the ownercx of a single-threaded scope * for which ownercx is waiting to become multi-threaded and shared, is cx. * That condition implies deadlock in ClaimScope if cx's thread were to wait * to share scope. * * (i) rt->gcLock held */ static JSBool WillDeadlock(JSScope *scope, JSContext *cx) { JSContext *ownercx; do { ownercx = scope->ownercx; if (ownercx == cx) { JS_RUNTIME_METER(cx->runtime, deadlocksAvoided); return JS_TRUE; } } while (ownercx && (scope = ownercx->scopeToShare) != NULL); return JS_FALSE; } /* * Make scope multi-threaded, i.e. share its ownership among contexts in rt * using a "thin" or (if necessary due to contention) "fat" lock. Called only * from ClaimScope, immediately below, when we detect deadlock were we to wait * for scope's lock, because its ownercx is waiting on a scope owned by the * calling cx. * * (i) rt->gcLock held */ static void ShareScope(JSRuntime *rt, JSScope *scope) { JSScope **todop; if (scope->u.link) { for (todop = &rt->scopeSharingTodo; *todop != scope; todop = &(*todop)->u.link) { JS_ASSERT(*todop != NO_SCOPE_SHARING_TODO); } *todop = scope->u.link; scope->u.link = NULL; /* null u.link for sanity ASAP */ JS_NOTIFY_ALL_CONDVAR(rt->scopeSharingDone); } js_InitLock(&scope->lock); if (scope == rt->setSlotScope) { /* * Nesting locks on another thread that's using scope->ownercx: give * the held lock a reentrancy count of 1 and set its lock.owner field * directly (no compare-and-swap needed while scope->ownercx is still * non-null). See below in ClaimScope, before the ShareScope call, * for more on why this is necessary. * * If NSPR_LOCK is defined, we cannot deadlock holding rt->gcLock and * acquiring scope->lock.fat here, against another thread holding that * fat lock and trying to grab rt->gcLock. This is because no other * thread can attempt to acquire scope->lock.fat until scope->ownercx * is null *and* our thread has released rt->gcLock, which interlocks * scope->ownercx's transition to null against tests of that member * in ClaimScope. */ scope->lock.owner = CX_THINLOCK_ID(scope->ownercx); #ifdef NSPR_LOCK JS_ACQUIRE_LOCK((JSLock*)scope->lock.fat); #endif scope->u.count = 1; } else { scope->u.count = 0; } js_FinishSharingScope(rt, scope); } /* * js_FinishSharingScope is the tail part of ShareScope, split out to become a * subroutine of JS_EndRequest too. The bulk of the work here involves making * mutable strings in the scope's object's slots be immutable. We have to do * this because such strings will soon be available to multiple threads, so * their buffers can't be realloc'd any longer in js_ConcatStrings, and their * members can't be modified by js_ConcatStrings, js_MinimizeDependentStrings, * or js_UndependString. * * The last bit of work done by js_FinishSharingScope nulls scope->ownercx and * updates rt->sharedScopes. */ #define MAKE_STRING_IMMUTABLE(rt, v, vp) \ JS_BEGIN_MACRO \ JSString *str_ = JSVAL_TO_STRING(v); \ uint8 *flagp_ = js_GetGCThingFlags(str_); \ if (*flagp_ & GCF_MUTABLE) { \ if (JSSTRING_IS_DEPENDENT(str_) && \ !js_UndependString(NULL, str_)) { \ JS_RUNTIME_METER(rt, badUndependStrings); \ *vp = JSVAL_VOID; \ } else { \ *flagp_ &= ~GCF_MUTABLE; \ } \ } \ JS_END_MACRO void js_FinishSharingScope(JSRuntime *rt, JSScope *scope) { JSObject *obj; uint32 nslots; jsval v, *vp, *end; obj = scope->object; nslots = JS_MIN(obj->map->freeslot, obj->map->nslots); for (vp = obj->slots, end = vp + nslots; vp < end; vp++) { v = *vp; if (JSVAL_IS_STRING(v)) MAKE_STRING_IMMUTABLE(rt, v, vp); } scope->ownercx = NULL; /* NB: set last, after lock init */ JS_RUNTIME_METER(rt, sharedScopes); } /* * Given a scope with apparently non-null ownercx different from cx, try to * set ownercx to cx, claiming exclusive (single-threaded) ownership of scope. * If we claim ownership, return true. Otherwise, we wait for ownercx to be * set to null (indicating that scope is multi-threaded); or if waiting would * deadlock, we set ownercx to null ourselves via ShareScope. In any case, * once ownercx is null we return false. */ static JSBool ClaimScope(JSScope *scope, JSContext *cx) { JSRuntime *rt; JSContext *ownercx; jsrefcount saveDepth; PRStatus stat; rt = cx->runtime; JS_RUNTIME_METER(rt, claimAttempts); JS_LOCK_GC(rt); /* Reload in case ownercx went away while we blocked on the lock. */ while ((ownercx = scope->ownercx) != NULL) { /* * Avoid selflock if ownercx is dead, or is not running a request, or * has the same thread as cx. Set scope->ownercx to cx so that the * matching JS_UNLOCK_SCOPE or JS_UNLOCK_OBJ macro call will take the * fast path around the corresponding js_UnlockScope or js_UnlockObj * function call. * * If scope->u.link is non-null, scope has already been inserted on * the rt->scopeSharingTodo list, because another thread's context * already wanted to lock scope while ownercx was running a request. * We can't claim any scope whose u.link is non-null at this point, * even if ownercx->requestDepth is 0 (see below where we suspend our * request before waiting on rt->scopeSharingDone). */ if (!scope->u.link && (!js_ValidContextPointer(rt, ownercx) || !ownercx->requestDepth || ownercx->thread == cx->thread)) { JS_ASSERT(scope->u.count == 0); scope->ownercx = cx; JS_UNLOCK_GC(rt); JS_RUNTIME_METER(rt, claimedScopes); return JS_TRUE; } /* * Avoid deadlock if scope's owner context is waiting on a scope that * we own, by revoking scope's ownership. This approach to deadlock * avoidance works because the engine never nests scope locks, except * for the notable case of js_SetProtoOrParent (see jsobj.c). * * If cx could hold locks on ownercx->scopeToShare, or if ownercx * could hold locks on scope, we would need to keep reentrancy counts * for all such "flyweight" (ownercx != NULL) locks, so that control * would unwind properly once these locks became "thin" or "fat". * Apart from the js_SetProtoOrParent exception, the engine promotes * a scope from exclusive to shared access only when locking, never * when holding or unlocking. * * If ownercx's thread is calling js_SetProtoOrParent, trying to lock * the inner scope (the scope of the object being set as the prototype * of the outer object), ShareScope will find the outer object's scope * at rt->setSlotScope. If it's the same as scope, we give it a lock * held by ownercx's thread with reentrancy count of 1, then we return * here and break. After that we unwind to js_[GS]etSlotThreadSafe or * js_LockScope (our caller), where we wait on the newly-fattened lock * until ownercx's thread unwinds from js_SetProtoOrParent. * * Avoid deadlock before any of this scope/context cycle detection if * cx is on the active GC's thread, because in that case, no requests * will run until the GC completes. Any scope wanted by the GC (from * a finalizer) that can't be claimed must be slated for sharing. */ if (rt->gcThread == cx->thread || (ownercx->scopeToShare && WillDeadlock(ownercx->scopeToShare, cx))) { ShareScope(rt, scope); break; } /* * Thanks to the non-zero NO_SCOPE_SHARING_TODO link terminator, we * can decide whether scope is on rt->scopeSharingTodo with a single * non-null test, and avoid double-insertion bugs. */ if (!scope->u.link) { scope->u.link = rt->scopeSharingTodo; rt->scopeSharingTodo = scope; js_HoldObjectMap(cx, &scope->map); } /* * Inline JS_SuspendRequest before we wait on rt->scopeSharingDone, * saving and clearing cx->requestDepth so we don't deadlock if the * GC needs to run on ownercx. * * Unlike JS_SuspendRequest and JS_EndRequest, we must take care not * to decrement rt->requestCount if cx is active on the GC's thread, * because the GC has already reduced rt->requestCount to exclude all * such such contexts. */ saveDepth = cx->requestDepth; if (saveDepth) { cx->requestDepth = 0; if (rt->gcThread != cx->thread) { JS_ASSERT(rt->requestCount > 0); rt->requestCount--; if (rt->requestCount == 0) JS_NOTIFY_REQUEST_DONE(rt); } } /* * We know that some other thread's context owns scope, which is now * linked onto rt->scopeSharingTodo, awaiting the end of that other * thread's request. So it is safe to wait on rt->scopeSharingDone. */ cx->scopeToShare = scope; stat = PR_WaitCondVar(rt->scopeSharingDone, PR_INTERVAL_NO_TIMEOUT); JS_ASSERT(stat != PR_FAILURE); /* * Inline JS_ResumeRequest after waiting on rt->scopeSharingDone, * restoring cx->requestDepth. Same note as above for the inlined, * specialized JS_SuspendRequest code: beware rt->gcThread. */ if (saveDepth) { if (rt->gcThread != cx->thread) { while (rt->gcLevel > 0) JS_AWAIT_GC_DONE(rt); rt->requestCount++; } cx->requestDepth = saveDepth; } /* * Don't clear cx->scopeToShare until after we're through waiting on * all condition variables protected by rt->gcLock -- that includes * rt->scopeSharingDone *and* rt->gcDone (hidden in JS_AWAIT_GC_DONE, * in the inlined JS_ResumeRequest code immediately above). * * Otherwise, the GC could easily deadlock with another thread that * owns a scope wanted by a finalizer. By keeping cx->scopeToShare * set till here, we ensure that such deadlocks are detected, which * results in the finalized object's scope being shared (it must, of * course, have other, live objects sharing it). */ cx->scopeToShare = NULL; } JS_UNLOCK_GC(rt); return JS_FALSE; } /* Exported to js.c, which calls it via OBJ_GET_* and JSVAL_IS_* macros. */ JS_FRIEND_API(jsval) js_GetSlotThreadSafe(JSContext *cx, JSObject *obj, uint32 slot) { jsval v; JSScope *scope; #ifndef NSPR_LOCK JSThinLock *tl; jsword me; #endif /* * We handle non-native objects via JSObjectOps.getRequiredSlot, treating * all slots starting from 0 as required slots. A property definition or * some prior arrangement must have allocated slot. * * Note once again (see jspubtd.h, before JSGetRequiredSlotOp's typedef) * the crucial distinction between a |required slot number| that's passed * to the get/setRequiredSlot JSObjectOps, and a |reserved slot index| * passed to the JS_Get/SetReservedSlot APIs. */ if (!OBJ_IS_NATIVE(obj)) return OBJ_GET_REQUIRED_SLOT(cx, obj, slot); /* * Native object locking is inlined here to optimize the single-threaded * and contention-free multi-threaded cases. */ scope = OBJ_SCOPE(obj); JS_ASSERT(scope->ownercx != cx); JS_ASSERT(obj->slots && slot < obj->map->freeslot); /* * Avoid locking if called from the GC (see GC_AWARE_GET_SLOT in jsobj.h). * Also avoid locking an object owning a sealed scope. If neither of those * special cases applies, try to claim scope's flyweight lock from whatever * context may have had it in an earlier request. */ if (CX_THREAD_IS_RUNNING_GC(cx) || (SCOPE_IS_SEALED(scope) && scope->object == obj) || (scope->ownercx && ClaimScope(scope, cx))) { return obj->slots[slot]; } #ifndef NSPR_LOCK tl = &scope->lock; me = CX_THINLOCK_ID(cx); JS_ASSERT(CURRENT_THREAD_IS_ME(me)); if (js_CompareAndSwap(&tl->owner, 0, me)) { /* * Got the lock with one compare-and-swap. Even so, someone else may * have mutated obj so it now has its own scope and lock, which would * require either a restart from the top of this routine, or a thin * lock release followed by fat lock acquisition. */ if (scope == OBJ_SCOPE(obj)) { v = obj->slots[slot]; if (!js_CompareAndSwap(&tl->owner, me, 0)) { /* Assert that scope locks never revert to flyweight. */ JS_ASSERT(scope->ownercx != cx); LOGIT(scope, '1'); scope->u.count = 1; js_UnlockObj(cx, obj); } return v; } if (!js_CompareAndSwap(&tl->owner, me, 0)) js_Dequeue(tl); } else if (Thin_RemoveWait(ReadWord(tl->owner)) == me) { return obj->slots[slot]; } #endif js_LockObj(cx, obj); v = obj->slots[slot]; /* * Test whether cx took ownership of obj's scope during js_LockObj. * * This does not mean that a given scope reverted to flyweight from "thin" * or "fat" -- it does mean that obj's map pointer changed due to another * thread setting a property, requiring obj to cease sharing a prototype * object's scope (whose lock was not flyweight, else we wouldn't be here * in the first place!). */ scope = OBJ_SCOPE(obj); if (scope->ownercx != cx) js_UnlockScope(cx, scope); return v; } void js_SetSlotThreadSafe(JSContext *cx, JSObject *obj, uint32 slot, jsval v) { JSScope *scope; #ifndef NSPR_LOCK JSThinLock *tl; jsword me; #endif /* Any string stored in a thread-safe object must be immutable. */ if (JSVAL_IS_STRING(v)) MAKE_STRING_IMMUTABLE(cx->runtime, v, &v); /* * We handle non-native objects via JSObjectOps.setRequiredSlot, as above * for the Get case. */ if (!OBJ_IS_NATIVE(obj)) { OBJ_SET_REQUIRED_SLOT(cx, obj, slot, v); return; } /* * Native object locking is inlined here to optimize the single-threaded * and contention-free multi-threaded cases. */ scope = OBJ_SCOPE(obj); JS_ASSERT(scope->ownercx != cx); JS_ASSERT(obj->slots && slot < obj->map->freeslot); /* * Avoid locking if called from the GC (see GC_AWARE_GET_SLOT in jsobj.h). * Also avoid locking an object owning a sealed scope. If neither of those * special cases applies, try to claim scope's flyweight lock from whatever * context may have had it in an earlier request. */ if (CX_THREAD_IS_RUNNING_GC(cx) || (SCOPE_IS_SEALED(scope) && scope->object == obj) || (scope->ownercx && ClaimScope(scope, cx))) { obj->slots[slot] = v; return; } #ifndef NSPR_LOCK tl = &scope->lock; me = CX_THINLOCK_ID(cx); JS_ASSERT(CURRENT_THREAD_IS_ME(me)); if (js_CompareAndSwap(&tl->owner, 0, me)) { if (scope == OBJ_SCOPE(obj)) { obj->slots[slot] = v; if (!js_CompareAndSwap(&tl->owner, me, 0)) { /* Assert that scope locks never revert to flyweight. */ JS_ASSERT(scope->ownercx != cx); LOGIT(scope, '1'); scope->u.count = 1; js_UnlockObj(cx, obj); } return; } if (!js_CompareAndSwap(&tl->owner, me, 0)) js_Dequeue(tl); } else if (Thin_RemoveWait(ReadWord(tl->owner)) == me) { obj->slots[slot] = v; return; } #endif js_LockObj(cx, obj); obj->slots[slot] = v; /* * Same drill as above, in js_GetSlotThreadSafe. Note that we cannot * assume obj has its own mutable scope (where scope->object == obj) yet, * because OBJ_SET_SLOT is called for the "universal", common slots such * as JSSLOT_PROTO and JSSLOT_PARENT, without a prior js_GetMutableScope. * See also the JSPROP_SHARED attribute and its usage. */ scope = OBJ_SCOPE(obj); if (scope->ownercx != cx) js_UnlockScope(cx, scope); } #ifndef NSPR_LOCK static JSFatLock * NewFatlock() { JSFatLock *fl = (JSFatLock *)malloc(sizeof(JSFatLock)); /* for now */ if (!fl) return NULL; fl->susp = 0; fl->next = NULL; fl->prevp = NULL; fl->slock = PR_NewLock(); fl->svar = PR_NewCondVar(fl->slock); return fl; } static void DestroyFatlock(JSFatLock *fl) { PR_DestroyLock(fl->slock); PR_DestroyCondVar(fl->svar); free(fl); } static JSFatLock * ListOfFatlocks(int listc) { JSFatLock *m; JSFatLock *m0; int i; JS_ASSERT(listc>0); m0 = m = NewFatlock(); for (i=1; inext = NewFatlock(); m = m->next; } return m0; } static void DeleteListOfFatlocks(JSFatLock *m) { JSFatLock *m0; for (; m; m=m0) { m0 = m->next; DestroyFatlock(m); } } static JSFatLockTable *fl_list_table = NULL; static uint32 fl_list_table_len = 0; static uint32 fl_list_chunk_len = 0; static JSFatLock * GetFatlock(void *id) { JSFatLock *m; uint32 i = GLOBAL_LOCK_INDEX(id); if (fl_list_table[i].free == NULL) { #ifdef DEBUG if (fl_list_table[i].taken) printf("Ran out of fat locks!\n"); #endif fl_list_table[i].free = ListOfFatlocks(fl_list_chunk_len); } m = fl_list_table[i].free; fl_list_table[i].free = m->next; m->susp = 0; m->next = fl_list_table[i].taken; m->prevp = &fl_list_table[i].taken; if (fl_list_table[i].taken) fl_list_table[i].taken->prevp = &m->next; fl_list_table[i].taken = m; return m; } static void PutFatlock(JSFatLock *m, void *id) { uint32 i; if (m == NULL) return; /* Unlink m from fl_list_table[i].taken. */ *m->prevp = m->next; if (m->next) m->next->prevp = m->prevp; /* Insert m in fl_list_table[i].free. */ i = GLOBAL_LOCK_INDEX(id); m->next = fl_list_table[i].free; fl_list_table[i].free = m; } #endif /* !NSPR_LOCK */ JSBool js_SetupLocks(int listc, int globc) { #ifndef NSPR_LOCK uint32 i; if (global_locks) return JS_TRUE; #ifdef DEBUG if (listc > 10000 || listc < 0) /* listc == fat lock list chunk length */ printf("Bad number %d in js_SetupLocks()!\n", listc); if (globc > 100 || globc < 0) /* globc == number of global locks */ printf("Bad number %d in js_SetupLocks()!\n", listc); #endif global_locks_log2 = JS_CeilingLog2(globc); global_locks_mask = JS_BITMASK(global_locks_log2); global_lock_count = JS_BIT(global_locks_log2); global_locks = (PRLock **) malloc(global_lock_count * sizeof(PRLock*)); if (!global_locks) return JS_FALSE; for (i = 0; i < global_lock_count; i++) { global_locks[i] = PR_NewLock(); if (!global_locks[i]) { global_lock_count = i; js_CleanupLocks(); return JS_FALSE; } } fl_list_table = (JSFatLockTable *) malloc(i * sizeof(JSFatLockTable)); if (!fl_list_table) { js_CleanupLocks(); return JS_FALSE; } fl_list_table_len = global_lock_count; for (i = 0; i < global_lock_count; i++) fl_list_table[i].free = fl_list_table[i].taken = NULL; fl_list_chunk_len = listc; #endif /* !NSPR_LOCK */ return JS_TRUE; } void js_CleanupLocks() { #ifndef NSPR_LOCK uint32 i; if (global_locks) { for (i = 0; i < global_lock_count; i++) PR_DestroyLock(global_locks[i]); free(global_locks); global_locks = NULL; global_lock_count = 1; global_locks_log2 = 0; global_locks_mask = 0; } if (fl_list_table) { for (i = 0; i < fl_list_table_len; i++) { DeleteListOfFatlocks(fl_list_table[i].free); fl_list_table[i].free = NULL; DeleteListOfFatlocks(fl_list_table[i].taken); fl_list_table[i].taken = NULL; } free(fl_list_table); fl_list_table = NULL; fl_list_table_len = 0; } #endif /* !NSPR_LOCK */ } #ifndef NSPR_LOCK /* * Fast locking and unlocking is implemented by delaying the allocation of a * system lock (fat lock) until contention. As long as a locking thread A * runs uncontended, the lock is represented solely by storing A's identity in * the object being locked. * * If another thread B tries to lock the object currently locked by A, B is * enqueued into a fat lock structure (which might have to be allocated and * pointed to by the object), and suspended using NSPR conditional variables * (wait). A wait bit (Bacon bit) is set in the lock word of the object, * signalling to A that when releasing the lock, B must be dequeued and * notified. * * The basic operation of the locking primitives (js_Lock, js_Unlock, * js_Enqueue, and js_Dequeue) is compare-and-swap. Hence, when locking into * the word pointed at by p, compare-and-swap(p, 0, A) success implies that p * is unlocked. Similarly, when unlocking p, if compare-and-swap(p, A, 0) * succeeds this implies that p is uncontended (no one is waiting because the * wait bit is not set). * * When dequeueing, the lock is released, and one of the threads suspended on * the lock is notified. If other threads still are waiting, the wait bit is * kept (in js_Enqueue), and if not, the fat lock is deallocated. * * The functions js_Enqueue, js_Dequeue, js_SuspendThread, and js_ResumeThread * are serialized using a global lock. For scalability, a hashtable of global * locks is used, which is indexed modulo the thin lock pointer. */ /* * Invariants: * (i) global lock is held * (ii) fl->susp >= 0 */ static int js_SuspendThread(JSThinLock *tl) { JSFatLock *fl; PRStatus stat; if (tl->fat == NULL) fl = tl->fat = GetFatlock(tl); else fl = tl->fat; JS_ASSERT(fl->susp >= 0); fl->susp++; PR_Lock(fl->slock); js_UnlockGlobal(tl); stat = PR_WaitCondVar(fl->svar, PR_INTERVAL_NO_TIMEOUT); JS_ASSERT(stat != PR_FAILURE); PR_Unlock(fl->slock); js_LockGlobal(tl); fl->susp--; if (fl->susp == 0) { PutFatlock(fl, tl); tl->fat = NULL; } return tl->fat == NULL; } /* * (i) global lock is held * (ii) fl->susp > 0 */ static void js_ResumeThread(JSThinLock *tl) { JSFatLock *fl = tl->fat; PRStatus stat; JS_ASSERT(fl != NULL); JS_ASSERT(fl->susp > 0); PR_Lock(fl->slock); js_UnlockGlobal(tl); stat = PR_NotifyCondVar(fl->svar); JS_ASSERT(stat != PR_FAILURE); PR_Unlock(fl->slock); } static void js_Enqueue(JSThinLock *tl, jsword me) { jsword o, n; js_LockGlobal(tl); for (;;) { o = ReadWord(tl->owner); n = Thin_SetWait(o); if (o != 0 && js_CompareAndSwap(&tl->owner, o, n)) { if (js_SuspendThread(tl)) me = Thin_RemoveWait(me); else me = Thin_SetWait(me); } else if (js_CompareAndSwap(&tl->owner, 0, me)) { js_UnlockGlobal(tl); return; } } } static void js_Dequeue(JSThinLock *tl) { jsword o; js_LockGlobal(tl); o = ReadWord(tl->owner); JS_ASSERT(Thin_GetWait(o) != 0); JS_ASSERT(tl->fat != NULL); if (!js_CompareAndSwap(&tl->owner, o, 0)) /* release it */ JS_ASSERT(0); js_ResumeThread(tl); } JS_INLINE void js_Lock(JSThinLock *tl, jsword me) { JS_ASSERT(CURRENT_THREAD_IS_ME(me)); if (js_CompareAndSwap(&tl->owner, 0, me)) return; if (Thin_RemoveWait(ReadWord(tl->owner)) != me) js_Enqueue(tl, me); #ifdef DEBUG else JS_ASSERT(0); #endif } JS_INLINE void js_Unlock(JSThinLock *tl, jsword me) { JS_ASSERT(CURRENT_THREAD_IS_ME(me)); /* * Only me can hold the lock, no need to use compare and swap atomic * operation for this common case. */ if (tl->owner == me) { tl->owner = 0; return; } JS_ASSERT(Thin_GetWait(tl->owner)); if (Thin_RemoveWait(ReadWord(tl->owner)) == me) js_Dequeue(tl); #ifdef DEBUG else JS_ASSERT(0); /* unbalanced unlock */ #endif } #endif /* !NSPR_LOCK */ void js_LockRuntime(JSRuntime *rt) { PR_Lock(rt->rtLock); #ifdef DEBUG rt->rtLockOwner = js_CurrentThreadId(); #endif } void js_UnlockRuntime(JSRuntime *rt) { #ifdef DEBUG rt->rtLockOwner = 0; #endif PR_Unlock(rt->rtLock); } void js_LockScope(JSContext *cx, JSScope *scope) { jsword me = CX_THINLOCK_ID(cx); JS_ASSERT(CURRENT_THREAD_IS_ME(me)); JS_ASSERT(scope->ownercx != cx); if (CX_THREAD_IS_RUNNING_GC(cx)) return; if (scope->ownercx && ClaimScope(scope, cx)) return; if (Thin_RemoveWait(ReadWord(scope->lock.owner)) == me) { JS_ASSERT(scope->u.count > 0); LOGIT(scope, '+'); scope->u.count++; } else { JSThinLock *tl = &scope->lock; JS_LOCK0(tl, me); JS_ASSERT(scope->u.count == 0); LOGIT(scope, '1'); scope->u.count = 1; } } void js_UnlockScope(JSContext *cx, JSScope *scope) { jsword me = CX_THINLOCK_ID(cx); /* We hope compilers use me instead of reloading cx->thread in the macro. */ if (CX_THREAD_IS_RUNNING_GC(cx)) return; if (cx->lockedSealedScope == scope) { cx->lockedSealedScope = NULL; return; } /* * If scope->ownercx is not null, it's likely that two contexts not using * requests nested locks for scope. The first context, cx here, claimed * scope; the second, scope->ownercx here, re-claimed it because the first * was not in a request, or was on the same thread. We don't want to keep * track of such nesting, because it penalizes the common non-nested case. * Instead of asserting here and silently coping, we simply re-claim scope * for cx and return. * * See http://bugzilla.mozilla.org/show_bug.cgi?id=229200 for a real world * case where an asymmetric thread model (Mozilla's main thread is known * to be the only thread that runs the GC) combined with multiple contexts * per thread has led to such request-less nesting. */ if (scope->ownercx) { JS_ASSERT(scope->u.count == 0); JS_ASSERT(scope->lock.owner == 0); scope->ownercx = cx; return; } JS_ASSERT(scope->u.count > 0); if (Thin_RemoveWait(ReadWord(scope->lock.owner)) != me) { JS_ASSERT(0); /* unbalanced unlock */ return; } LOGIT(scope, '-'); if (--scope->u.count == 0) { JSThinLock *tl = &scope->lock; JS_UNLOCK0(tl, me); } } /* * NB: oldscope may be null if our caller is js_GetMutableScope and it just * dropped the last reference to oldscope. */ void js_TransferScopeLock(JSContext *cx, JSScope *oldscope, JSScope *newscope) { jsword me; JSThinLock *tl; JS_ASSERT(JS_IS_SCOPE_LOCKED(cx, newscope)); /* * If the last reference to oldscope went away, newscope needs no lock * state update. */ if (!oldscope) return; JS_ASSERT(JS_IS_SCOPE_LOCKED(cx, oldscope)); /* * Special case in js_LockScope and js_UnlockScope for the GC calling * code that locks, unlocks, or mutates. Nothing to do in these cases, * because scope and newscope were "locked" by the GC thread, so neither * was actually locked. */ if (CX_THREAD_IS_RUNNING_GC(cx)) return; /* * Special case in js_LockObj and js_UnlockScope for locking the sealed * scope of an object that owns that scope (the prototype or mutated obj * for which OBJ_SCOPE(obj)->object == obj), and unlocking it. */ JS_ASSERT(cx->lockedSealedScope != newscope); if (cx->lockedSealedScope == oldscope) { JS_ASSERT(newscope->ownercx == cx || (!newscope->ownercx && newscope->u.count == 1)); cx->lockedSealedScope = NULL; return; } /* * If oldscope is single-threaded, there's nothing to do. */ if (oldscope->ownercx) { JS_ASSERT(oldscope->ownercx == cx); JS_ASSERT(newscope->ownercx == cx || (!newscope->ownercx && newscope->u.count == 1)); return; } /* * We transfer oldscope->u.count only if newscope is not single-threaded. * Flow unwinds from here through some number of JS_UNLOCK_SCOPE and/or * JS_UNLOCK_OBJ macro calls, which will decrement newscope->u.count only * if they find newscope->ownercx != cx. */ if (newscope->ownercx != cx) { JS_ASSERT(!newscope->ownercx); newscope->u.count = oldscope->u.count; } /* * Reset oldscope's lock state so that it is completely unlocked. */ LOGIT(oldscope, '0'); oldscope->u.count = 0; tl = &oldscope->lock; me = CX_THINLOCK_ID(cx); JS_UNLOCK0(tl, me); } void js_LockObj(JSContext *cx, JSObject *obj) { JSScope *scope; JS_ASSERT(OBJ_IS_NATIVE(obj)); /* * We must test whether the GC is calling and return without mutating any * state, especially cx->lockedSealedScope. Note asymmetry with respect to * js_UnlockObj, which is a thin-layer on top of js_UnlockScope. */ if (CX_THREAD_IS_RUNNING_GC(cx)) return; for (;;) { scope = OBJ_SCOPE(obj); if (SCOPE_IS_SEALED(scope) && scope->object == obj && !cx->lockedSealedScope) { cx->lockedSealedScope = scope; return; } js_LockScope(cx, scope); /* If obj still has this scope, we're done. */ if (scope == OBJ_SCOPE(obj)) return; /* Lost a race with a mutator; retry with obj's new scope. */ js_UnlockScope(cx, scope); } } void js_UnlockObj(JSContext *cx, JSObject *obj) { JS_ASSERT(OBJ_IS_NATIVE(obj)); js_UnlockScope(cx, OBJ_SCOPE(obj)); } #ifdef DEBUG JSBool js_IsRuntimeLocked(JSRuntime *rt) { return js_CurrentThreadId() == rt->rtLockOwner; } JSBool js_IsObjLocked(JSContext *cx, JSObject *obj) { JSScope *scope = OBJ_SCOPE(obj); return MAP_IS_NATIVE(&scope->map) && js_IsScopeLocked(cx, scope); } JSBool js_IsScopeLocked(JSContext *cx, JSScope *scope) { /* Special case: the GC locking any object's scope, see js_LockScope. */ if (CX_THREAD_IS_RUNNING_GC(cx)) return JS_TRUE; /* Special case: locked object owning a sealed scope, see js_LockObj. */ if (cx->lockedSealedScope == scope) return JS_TRUE; /* * General case: the scope is either exclusively owned (by cx), or it has * a thin or fat lock to cope with shared (concurrent) ownership. */ if (scope->ownercx) { JS_ASSERT(scope->ownercx == cx || scope->ownercx->thread == cx->thread); return JS_TRUE; } return js_CurrentThreadId() == ((JSThread *)Thin_RemoveWait(ReadWord(scope->lock.owner)))->id; } #endif /* DEBUG */ #endif /* JS_THREADSAFE */ pacparser-1.4.5/src/spidermonkey/js/src/jslock.h000066400000000000000000000253121464010763600216540ustar00rootroot00000000000000/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- * * ***** BEGIN LICENSE BLOCK ***** * Version: MPL 1.1/GPL 2.0/LGPL 2.1 * * The contents of this file are subject to the Mozilla Public License Version * 1.1 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * http://www.mozilla.org/MPL/ * * Software distributed under the License is distributed on an "AS IS" basis, * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License * for the specific language governing rights and limitations under the * License. * * The Original Code is Mozilla Communicator client code, released * March 31, 1998. * * The Initial Developer of the Original Code is * Netscape Communications Corporation. * Portions created by the Initial Developer are Copyright (C) 1998 * the Initial Developer. All Rights Reserved. * * Contributor(s): * * Alternatively, the contents of this file may be used under the terms of * either of the GNU General Public License Version 2 or later (the "GPL"), * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), * in which case the provisions of the GPL or the LGPL are applicable instead * of those above. If you wish to allow use of your version of this file only * under the terms of either the GPL or the LGPL, and not to allow others to * use your version of this file under the terms of the MPL, indicate your * decision by deleting the provisions above and replace them with the notice * and other provisions required by the GPL or the LGPL. If you do not delete * the provisions above, a recipient may use your version of this file under * the terms of any one of the MPL, the GPL or the LGPL. * * ***** END LICENSE BLOCK ***** */ #ifndef jslock_h__ #define jslock_h__ #ifdef JS_THREADSAFE #include "jstypes.h" #include "pratom.h" #include "prlock.h" #include "prcvar.h" #include "prthread.h" #include "jsprvtd.h" /* for JSScope, etc. */ #include "jspubtd.h" /* for JSRuntime, etc. */ #define Thin_GetWait(W) ((jsword)(W) & 0x1) #define Thin_SetWait(W) ((jsword)(W) | 0x1) #define Thin_RemoveWait(W) ((jsword)(W) & ~0x1) typedef struct JSFatLock JSFatLock; struct JSFatLock { int susp; PRLock *slock; PRCondVar *svar; JSFatLock *next; JSFatLock **prevp; }; typedef struct JSThinLock { jsword owner; JSFatLock *fat; } JSThinLock; #define CX_THINLOCK_ID(cx) ((jsword)(cx)->thread) #define CURRENT_THREAD_IS_ME(me) (((JSThread *)me)->id == js_CurrentThreadId()) typedef PRLock JSLock; typedef struct JSFatLockTable { JSFatLock *free; JSFatLock *taken; } JSFatLockTable; /* * Atomic increment and decrement for a reference counter, given jsrefcount *p. * NB: jsrefcount is int32, aka PRInt32, so that pratom.h functions work. */ #define JS_ATOMIC_INCREMENT(p) PR_AtomicIncrement((PRInt32 *)(p)) #define JS_ATOMIC_DECREMENT(p) PR_AtomicDecrement((PRInt32 *)(p)) #define JS_ATOMIC_ADD(p,v) PR_AtomicAdd((PRInt32 *)(p), (PRInt32)(v)) #define js_CurrentThreadId() (jsword)PR_GetCurrentThread() #define JS_NEW_LOCK() PR_NewLock() #define JS_DESTROY_LOCK(l) PR_DestroyLock(l) #define JS_ACQUIRE_LOCK(l) PR_Lock(l) #define JS_RELEASE_LOCK(l) PR_Unlock(l) #define JS_LOCK0(P,M) js_Lock(P,M) #define JS_UNLOCK0(P,M) js_Unlock(P,M) #define JS_NEW_CONDVAR(l) PR_NewCondVar(l) #define JS_DESTROY_CONDVAR(cv) PR_DestroyCondVar(cv) #define JS_WAIT_CONDVAR(cv,to) PR_WaitCondVar(cv,to) #define JS_NO_TIMEOUT PR_INTERVAL_NO_TIMEOUT #define JS_NOTIFY_CONDVAR(cv) PR_NotifyCondVar(cv) #define JS_NOTIFY_ALL_CONDVAR(cv) PR_NotifyAllCondVar(cv) /* * Include jsscope.h so JS_LOCK_OBJ macro callers don't have to include it. * Since there is a JSThinLock member in JSScope, we can't nest this include * much earlier (see JSThinLock's typedef, above). Yes, that means there is * an #include cycle between jslock.h and jsscope.h: moderate-sized XXX here, * to be fixed by moving JS_LOCK_SCOPE to jsscope.h, JS_LOCK_OBJ to jsobj.h, * and so on. */ #include "jsscope.h" #define JS_LOCK_RUNTIME(rt) js_LockRuntime(rt) #define JS_UNLOCK_RUNTIME(rt) js_UnlockRuntime(rt) /* * NB: The JS_LOCK_OBJ and JS_UNLOCK_OBJ macros work *only* on native objects * (objects for which OBJ_IS_NATIVE returns true). All uses of these macros in * the engine are predicated on OBJ_IS_NATIVE or equivalent checks. These uses * are for optimizations above the JSObjectOps layer, under which object locks * normally hide. */ #define JS_LOCK_OBJ(cx,obj) ((OBJ_SCOPE(obj)->ownercx == (cx)) \ ? (void)0 \ : (js_LockObj(cx, obj))) #define JS_UNLOCK_OBJ(cx,obj) ((OBJ_SCOPE(obj)->ownercx == (cx)) \ ? (void)0 : js_UnlockObj(cx, obj)) #define JS_LOCK_SCOPE(cx,scope) ((scope)->ownercx == (cx) ? (void)0 \ : js_LockScope(cx, scope)) #define JS_UNLOCK_SCOPE(cx,scope) ((scope)->ownercx == (cx) ? (void)0 \ : js_UnlockScope(cx, scope)) #define JS_TRANSFER_SCOPE_LOCK(cx, scope, newscope) \ js_TransferScopeLock(cx, scope, newscope) extern void js_LockRuntime(JSRuntime *rt); extern void js_UnlockRuntime(JSRuntime *rt); extern void js_LockObj(JSContext *cx, JSObject *obj); extern void js_UnlockObj(JSContext *cx, JSObject *obj); extern void js_LockScope(JSContext *cx, JSScope *scope); extern void js_UnlockScope(JSContext *cx, JSScope *scope); extern int js_SetupLocks(int,int); extern void js_CleanupLocks(); extern void js_TransferScopeLock(JSContext *, JSScope *, JSScope *); extern JS_FRIEND_API(jsval) js_GetSlotThreadSafe(JSContext *, JSObject *, uint32); extern void js_SetSlotThreadSafe(JSContext *, JSObject *, uint32, jsval); extern void js_InitLock(JSThinLock *); extern void js_FinishLock(JSThinLock *); extern void js_FinishSharingScope(JSRuntime *rt, JSScope *scope); #ifdef DEBUG #define JS_IS_RUNTIME_LOCKED(rt) js_IsRuntimeLocked(rt) #define JS_IS_OBJ_LOCKED(cx,obj) js_IsObjLocked(cx,obj) #define JS_IS_SCOPE_LOCKED(cx,scope) js_IsScopeLocked(cx,scope) extern JSBool js_IsRuntimeLocked(JSRuntime *rt); extern JSBool js_IsObjLocked(JSContext *cx, JSObject *obj); extern JSBool js_IsScopeLocked(JSContext *cx, JSScope *scope); #else #define JS_IS_RUNTIME_LOCKED(rt) 0 #define JS_IS_OBJ_LOCKED(cx,obj) 1 #define JS_IS_SCOPE_LOCKED(cx,scope) 1 #endif /* DEBUG */ #define JS_LOCK_OBJ_VOID(cx, obj, e) \ JS_BEGIN_MACRO \ JS_LOCK_OBJ(cx, obj); \ e; \ JS_UNLOCK_OBJ(cx, obj); \ JS_END_MACRO #define JS_LOCK_VOID(cx, e) \ JS_BEGIN_MACRO \ JSRuntime *_rt = (cx)->runtime; \ JS_LOCK_RUNTIME_VOID(_rt, e); \ JS_END_MACRO /* FIXME: bug 353962 hackaround */ #define JS_USE_ONLY_NSPR_LOCKS 1 #if defined(JS_USE_ONLY_NSPR_LOCKS) || \ !( (defined(_WIN32) && defined(_M_IX86)) || \ (defined(__GNUC__) && defined(__i386__)) || \ ((defined(__USLC__) || defined(_SCO_DS)) && defined(i386)) || \ (defined(SOLARIS) && defined(sparc) && defined(ULTRA_SPARC)) || \ defined(AIX) ) #define NSPR_LOCK 1 #undef JS_LOCK0 #undef JS_UNLOCK0 #define JS_LOCK0(P,M) (JS_ACQUIRE_LOCK(((JSLock*)(P)->fat)), (P)->owner = (M)) #define JS_UNLOCK0(P,M) ((P)->owner = 0, JS_RELEASE_LOCK(((JSLock*)(P)->fat))) #else /* arch-tests */ #undef NSPR_LOCK extern JS_INLINE void js_Lock(JSThinLock *tl, jsword me); extern JS_INLINE void js_Unlock(JSThinLock *tl, jsword me); #endif /* arch-tests */ #else /* !JS_THREADSAFE */ #define JS_ATOMIC_INCREMENT(p) (++*(p)) #define JS_ATOMIC_DECREMENT(p) (--*(p)) #define JS_ATOMIC_ADD(p,v) (*(p) += (v)) #define JS_CurrentThreadId() 0 #define JS_NEW_LOCK() NULL #define JS_DESTROY_LOCK(l) ((void)0) #define JS_ACQUIRE_LOCK(l) ((void)0) #define JS_RELEASE_LOCK(l) ((void)0) #define JS_LOCK0(P,M) ((void)0) #define JS_UNLOCK0(P,M) ((void)0) #define JS_NEW_CONDVAR(l) NULL #define JS_DESTROY_CONDVAR(cv) ((void)0) #define JS_WAIT_CONDVAR(cv,to) ((void)0) #define JS_NOTIFY_CONDVAR(cv) ((void)0) #define JS_NOTIFY_ALL_CONDVAR(cv) ((void)0) #define JS_LOCK_RUNTIME(rt) ((void)0) #define JS_UNLOCK_RUNTIME(rt) ((void)0) #define JS_LOCK_OBJ(cx,obj) ((void)0) #define JS_UNLOCK_OBJ(cx,obj) ((void)0) #define JS_LOCK_OBJ_VOID(cx,obj,e) (e) #define JS_LOCK_SCOPE(cx,scope) ((void)0) #define JS_UNLOCK_SCOPE(cx,scope) ((void)0) #define JS_TRANSFER_SCOPE_LOCK(c,o,n) ((void)0) #define JS_IS_RUNTIME_LOCKED(rt) 1 #define JS_IS_OBJ_LOCKED(cx,obj) 1 #define JS_IS_SCOPE_LOCKED(cx,scope) 1 #define JS_LOCK_VOID(cx, e) JS_LOCK_RUNTIME_VOID((cx)->runtime, e) #endif /* !JS_THREADSAFE */ #define JS_LOCK_RUNTIME_VOID(rt,e) \ JS_BEGIN_MACRO \ JS_LOCK_RUNTIME(rt); \ e; \ JS_UNLOCK_RUNTIME(rt); \ JS_END_MACRO #define JS_LOCK_GC(rt) JS_ACQUIRE_LOCK((rt)->gcLock) #define JS_UNLOCK_GC(rt) JS_RELEASE_LOCK((rt)->gcLock) #define JS_LOCK_GC_VOID(rt,e) (JS_LOCK_GC(rt), (e), JS_UNLOCK_GC(rt)) #define JS_AWAIT_GC_DONE(rt) JS_WAIT_CONDVAR((rt)->gcDone, JS_NO_TIMEOUT) #define JS_NOTIFY_GC_DONE(rt) JS_NOTIFY_ALL_CONDVAR((rt)->gcDone) #define JS_AWAIT_REQUEST_DONE(rt) JS_WAIT_CONDVAR((rt)->requestDone, \ JS_NO_TIMEOUT) #define JS_NOTIFY_REQUEST_DONE(rt) JS_NOTIFY_CONDVAR((rt)->requestDone) #define JS_LOCK(P,CX) JS_LOCK0(P, CX_THINLOCK_ID(CX)) #define JS_UNLOCK(P,CX) JS_UNLOCK0(P, CX_THINLOCK_ID(CX)) #endif /* jslock_h___ */ pacparser-1.4.5/src/spidermonkey/js/src/jslocko.asm000066400000000000000000000043631464010763600223670ustar00rootroot00000000000000; -*- Mode: asm; tab-width: 8; c-basic-offset: 4 -*- ; ***** BEGIN LICENSE BLOCK ***** ; Version: MPL 1.1/GPL 2.0/LGPL 2.1 ; ; The contents of this file are subject to the Mozilla Public License Version ; 1.1 (the "License"); you may not use this file except in compliance with ; the License. You may obtain a copy of the License at ; http://www.mozilla.org/MPL/ ; ; Software distributed under the License is distributed on an "AS IS" basis, ; WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License ; for the specific language governing rights and limitations under the ; License. ; ; The Original Code is an OS/2 implementation of js_CompareAndSwap in assembly. ; ; The Initial Developer of the Original Code is ; IBM Corporation. ; Portions created by the Initial Developer are Copyright (C) 2001 ; the Initial Developer. All Rights Reserved. ; ; Contributor(s): ; ; Alternatively, the contents of this file may be used under the terms of ; either the GNU General Public License Version 2 or later (the "GPL"), or ; the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), ; in which case the provisions of the GPL or the LGPL are applicable instead ; of those above. If you wish to allow use of your version of this file only ; under the terms of either the GPL or the LGPL, and not to allow others to ; use your version of this file under the terms of the MPL, indicate your ; decision by deleting the provisions above and replace them with the notice ; and other provisions required by the GPL or the LGPL. If you do not delete ; the provisions above, a recipient may use your version of this file under ; the terms of any one of the MPL, the GPL or the LGPL. ; ; ***** END LICENSE BLOCK ***** .486P .MODEL FLAT, OPTLINK .STACK .CODE ;;;--------------------------------------------------------------------- ;;; int _Optlink js_CompareAndSwap(jsword *w, jsword ov, jsword nv) ;;;--------------------------------------------------------------------- js_CompareAndSwap PROC OPTLINK EXPORT push ebx mov ebx, eax mov eax, edx mov edx, ebx lock cmpxchg [ebx], ecx sete al and eax, 1h pop ebx ret js_CompareAndSwap endp END pacparser-1.4.5/src/spidermonkey/js/src/jslog2.c000066400000000000000000000055211464010763600215620ustar00rootroot00000000000000/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ /* ***** BEGIN LICENSE BLOCK ***** * Version: MPL 1.1/GPL 2.0/LGPL 2.1 * * The contents of this file are subject to the Mozilla Public License Version * 1.1 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * http://www.mozilla.org/MPL/ * * Software distributed under the License is distributed on an "AS IS" basis, * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License * for the specific language governing rights and limitations under the * License. * * The Original Code is Mozilla Communicator client code, released * March 31, 1998. * * The Initial Developer of the Original Code is * Netscape Communications Corporation. * Portions created by the Initial Developer are Copyright (C) 1998 * the Initial Developer. All Rights Reserved. * * Contributor(s): * * Alternatively, the contents of this file may be used under the terms of * either of the GNU General Public License Version 2 or later (the "GPL"), * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), * in which case the provisions of the GPL or the LGPL are applicable instead * of those above. If you wish to allow use of your version of this file only * under the terms of either the GPL or the LGPL, and not to allow others to * use your version of this file under the terms of the MPL, indicate your * decision by deleting the provisions above and replace them with the notice * and other provisions required by the GPL or the LGPL. If you do not delete * the provisions above, a recipient may use your version of this file under * the terms of any one of the MPL, the GPL or the LGPL. * * ***** END LICENSE BLOCK ***** */ #include "jsstddef.h" #include "jsbit.h" #include "jsutil.h" /* ** Compute the log of the least power of 2 greater than or equal to n */ JS_PUBLIC_API(JSIntn) JS_CeilingLog2(JSUint32 n) { JSIntn log2; JS_CEILING_LOG2(log2, n); return log2; } /* ** Compute the log of the greatest power of 2 less than or equal to n. ** This really just finds the highest set bit in the word. */ JS_PUBLIC_API(JSIntn) JS_FloorLog2(JSUint32 n) { JSIntn log2; JS_FLOOR_LOG2(log2, n); return log2; } /* * js_FloorLog2wImpl has to be defined only for 64-bit non-GCC case. */ #if !defined(JS_HAS_GCC_BUILTIN_CLZ) && JS_BYTES_PER_WORD == 8 JSUword js_FloorLog2wImpl(JSUword n) { JSUword log2, m; JS_ASSERT(n != 0); log2 = 0; m = n >> 32; if (m != 0) { n = m; log2 = 32; } m = n >> 16; if (m != 0) { n = m; log2 |= 16; } m = n >> 8; if (m != 0) { n = m; log2 |= 8; } m = n >> 4; if (m != 0) { n = m; log2 |= 4; } m = n >> 2; if (m != 0) { n = m; log2 |= 2; } log2 |= (n >> 1); return log2; } #endif pacparser-1.4.5/src/spidermonkey/js/src/jslong.c000066400000000000000000000202261464010763600216550ustar00rootroot00000000000000/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* ***** BEGIN LICENSE BLOCK ***** * Version: MPL 1.1/GPL 2.0/LGPL 2.1 * * The contents of this file are subject to the Mozilla Public License Version * 1.1 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * http://www.mozilla.org/MPL/ * * Software distributed under the License is distributed on an "AS IS" basis, * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License * for the specific language governing rights and limitations under the * License. * * The Original Code is Mozilla Communicator client code, released * March 31, 1998. * * The Initial Developer of the Original Code is * Netscape Communications Corporation. * Portions created by the Initial Developer are Copyright (C) 1998 * the Initial Developer. All Rights Reserved. * * Contributor(s): * * Alternatively, the contents of this file may be used under the terms of * either of the GNU General Public License Version 2 or later (the "GPL"), * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), * in which case the provisions of the GPL or the LGPL are applicable instead * of those above. If you wish to allow use of your version of this file only * under the terms of either the GPL or the LGPL, and not to allow others to * use your version of this file under the terms of the MPL, indicate your * decision by deleting the provisions above and replace them with the notice * and other provisions required by the GPL or the LGPL. If you do not delete * the provisions above, a recipient may use your version of this file under * the terms of any one of the MPL, the GPL or the LGPL. * * ***** END LICENSE BLOCK ***** */ #include "jsstddef.h" #include "jstypes.h" #include "jslong.h" static JSInt64 ll_zero = JSLL_INIT( 0x00000000,0x00000000 ); static JSInt64 ll_maxint = JSLL_INIT( 0x7fffffff, 0xffffffff ); static JSInt64 ll_minint = JSLL_INIT( 0x80000000, 0x00000000 ); #ifdef HAVE_WATCOM_BUG_2 JSInt64 __pascal __loadds __export JSLL_Zero(void) { return ll_zero; } JSInt64 __pascal __loadds __export JSLL_MaxInt(void) { return ll_maxint; } JSInt64 __pascal __loadds __export JSLL_MinInt(void) { return ll_minint; } #else JS_PUBLIC_API(JSInt64) JSLL_Zero(void) { return ll_zero; } JS_PUBLIC_API(JSInt64) JSLL_MaxInt(void) { return ll_maxint; } JS_PUBLIC_API(JSInt64) JSLL_MinInt(void) { return ll_minint; } #endif #ifndef JS_HAVE_LONG_LONG /* ** Divide 64-bit a by 32-bit b, which must be normalized so its high bit is 1. */ static void norm_udivmod32(JSUint32 *qp, JSUint32 *rp, JSUint64 a, JSUint32 b) { JSUint32 d1, d0, q1, q0; JSUint32 r1, r0, m; d1 = jshi16(b); d0 = jslo16(b); r1 = a.hi % d1; q1 = a.hi / d1; m = q1 * d0; r1 = (r1 << 16) | jshi16(a.lo); if (r1 < m) { q1--, r1 += b; if (r1 >= b /* i.e., we didn't get a carry when adding to r1 */ && r1 < m) { q1--, r1 += b; } } r1 -= m; r0 = r1 % d1; q0 = r1 / d1; m = q0 * d0; r0 = (r0 << 16) | jslo16(a.lo); if (r0 < m) { q0--, r0 += b; if (r0 >= b && r0 < m) { q0--, r0 += b; } } *qp = (q1 << 16) | q0; *rp = r0 - m; } static JSUint32 CountLeadingZeros(JSUint32 a) { JSUint32 t; JSUint32 r = 32; if ((t = a >> 16) != 0) r -= 16, a = t; if ((t = a >> 8) != 0) r -= 8, a = t; if ((t = a >> 4) != 0) r -= 4, a = t; if ((t = a >> 2) != 0) r -= 2, a = t; if ((t = a >> 1) != 0) r -= 1, a = t; if (a & 1) r--; return r; } JS_PUBLIC_API(void) jsll_udivmod(JSUint64 *qp, JSUint64 *rp, JSUint64 a, JSUint64 b) { JSUint32 n0, n1, n2; JSUint32 q0, q1; JSUint32 rsh, lsh; n0 = a.lo; n1 = a.hi; if (b.hi == 0) { if (b.lo > n1) { /* (0 q0) = (n1 n0) / (0 D0) */ lsh = CountLeadingZeros(b.lo); if (lsh) { /* * Normalize, i.e. make the most significant bit of the * denominator be set. */ b.lo = b.lo << lsh; n1 = (n1 << lsh) | (n0 >> (32 - lsh)); n0 = n0 << lsh; } a.lo = n0, a.hi = n1; norm_udivmod32(&q0, &n0, a, b.lo); q1 = 0; /* remainder is in n0 >> lsh */ } else { /* (q1 q0) = (n1 n0) / (0 d0) */ if (b.lo == 0) /* user wants to divide by zero! */ b.lo = 1 / b.lo; /* so go ahead and crash */ lsh = CountLeadingZeros(b.lo); if (lsh == 0) { /* * From (n1 >= b.lo) * && (the most significant bit of b.lo is set), * conclude that * (the most significant bit of n1 is set) * && (the leading quotient digit q1 = 1). * * This special case is necessary, not an optimization * (Shifts counts of 32 are undefined). */ n1 -= b.lo; q1 = 1; } else { /* * Normalize. */ rsh = 32 - lsh; b.lo = b.lo << lsh; n2 = n1 >> rsh; n1 = (n1 << lsh) | (n0 >> rsh); n0 = n0 << lsh; a.lo = n1, a.hi = n2; norm_udivmod32(&q1, &n1, a, b.lo); } /* n1 != b.lo... */ a.lo = n0, a.hi = n1; norm_udivmod32(&q0, &n0, a, b.lo); /* remainder in n0 >> lsh */ } if (rp) { rp->lo = n0 >> lsh; rp->hi = 0; } } else { if (b.hi > n1) { /* (0 0) = (n1 n0) / (D1 d0) */ q0 = 0; q1 = 0; /* remainder in (n1 n0) */ if (rp) { rp->lo = n0; rp->hi = n1; } } else { /* (0 q0) = (n1 n0) / (d1 d0) */ lsh = CountLeadingZeros(b.hi); if (lsh == 0) { /* * From (n1 >= b.hi) * && (the most significant bit of b.hi is set), * conclude that * (the most significant bit of n1 is set) * && (the quotient digit q0 = 0 or 1). * * This special case is necessary, not an optimization. */ /* * The condition on the next line takes advantage of that * n1 >= b.hi (true due to control flow). */ if (n1 > b.hi || n0 >= b.lo) { q0 = 1; a.lo = n0, a.hi = n1; JSLL_SUB(a, a, b); } else { q0 = 0; } q1 = 0; if (rp) { rp->lo = n0; rp->hi = n1; } } else { JSInt64 m; /* * Normalize. */ rsh = 32 - lsh; b.hi = (b.hi << lsh) | (b.lo >> rsh); b.lo = b.lo << lsh; n2 = n1 >> rsh; n1 = (n1 << lsh) | (n0 >> rsh); n0 = n0 << lsh; a.lo = n1, a.hi = n2; norm_udivmod32(&q0, &n1, a, b.hi); JSLL_MUL32(m, q0, b.lo); if ((m.hi > n1) || ((m.hi == n1) && (m.lo > n0))) { q0--; JSLL_SUB(m, m, b); } q1 = 0; /* Remainder is ((n1 n0) - (m1 m0)) >> lsh */ if (rp) { a.lo = n0, a.hi = n1; JSLL_SUB(a, a, m); rp->lo = (a.hi << rsh) | (a.lo >> lsh); rp->hi = a.hi >> lsh; } } } } if (qp) { qp->lo = q0; qp->hi = q1; } } #endif /* !JS_HAVE_LONG_LONG */ pacparser-1.4.5/src/spidermonkey/js/src/jslong.h000066400000000000000000000345261464010763600216720ustar00rootroot00000000000000/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* ***** BEGIN LICENSE BLOCK ***** * Version: MPL 1.1/GPL 2.0/LGPL 2.1 * * The contents of this file are subject to the Mozilla Public License Version * 1.1 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * http://www.mozilla.org/MPL/ * * Software distributed under the License is distributed on an "AS IS" basis, * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License * for the specific language governing rights and limitations under the * License. * * The Original Code is Mozilla Communicator client code, released * March 31, 1998. * * The Initial Developer of the Original Code is * Netscape Communications Corporation. * Portions created by the Initial Developer are Copyright (C) 1998 * the Initial Developer. All Rights Reserved. * * Contributor(s): * * Alternatively, the contents of this file may be used under the terms of * either of the GNU General Public License Version 2 or later (the "GPL"), * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), * in which case the provisions of the GPL or the LGPL are applicable instead * of those above. If you wish to allow use of your version of this file only * under the terms of either the GPL or the LGPL, and not to allow others to * use your version of this file under the terms of the MPL, indicate your * decision by deleting the provisions above and replace them with the notice * and other provisions required by the GPL or the LGPL. If you do not delete * the provisions above, a recipient may use your version of this file under * the terms of any one of the MPL, the GPL or the LGPL. * * ***** END LICENSE BLOCK ***** */ /* ** File: jslong.h ** Description: Portable access to 64 bit numerics ** ** Long-long (64-bit signed integer type) support. Some C compilers ** don't support 64 bit integers yet, so we use these macros to ** support both machines that do and don't. **/ #ifndef jslong_h___ #define jslong_h___ #include "jstypes.h" JS_BEGIN_EXTERN_C /*********************************************************************** ** DEFINES: JSLL_MaxInt ** JSLL_MinInt ** JSLL_Zero ** DESCRIPTION: ** Various interesting constants and static variable ** initializer ***********************************************************************/ #ifdef HAVE_WATCOM_BUG_2 JSInt64 __pascal __loadds __export JSLL_MaxInt(void); JSInt64 __pascal __loadds __export JSLL_MinInt(void); JSInt64 __pascal __loadds __export JSLL_Zero(void); #else extern JS_PUBLIC_API(JSInt64) JSLL_MaxInt(void); extern JS_PUBLIC_API(JSInt64) JSLL_MinInt(void); extern JS_PUBLIC_API(JSInt64) JSLL_Zero(void); #endif #define JSLL_MAXINT JSLL_MaxInt() #define JSLL_MININT JSLL_MinInt() #define JSLL_ZERO JSLL_Zero() #ifdef JS_HAVE_LONG_LONG #if JS_BYTES_PER_LONG == 8 #define JSLL_INIT(hi, lo) ((hi ## L << 32) + lo ## L) #elif (defined(WIN32) || defined(WIN16)) && !defined(__GNUC__) #define JSLL_INIT(hi, lo) ((hi ## i64 << 32) + lo ## i64) #else #define JSLL_INIT(hi, lo) ((hi ## LL << 32) + lo ## LL) #endif /*********************************************************************** ** MACROS: JSLL_* ** DESCRIPTION: ** The following macros define portable access to the 64 bit ** math facilities. ** ***********************************************************************/ /*********************************************************************** ** MACROS: JSLL_ ** ** JSLL_IS_ZERO Test for zero ** JSLL_EQ Test for equality ** JSLL_NE Test for inequality ** JSLL_GE_ZERO Test for zero or positive ** JSLL_CMP Compare two values ***********************************************************************/ #define JSLL_IS_ZERO(a) ((a) == 0) #define JSLL_EQ(a, b) ((a) == (b)) #define JSLL_NE(a, b) ((a) != (b)) #define JSLL_GE_ZERO(a) ((a) >= 0) #define JSLL_CMP(a, op, b) ((JSInt64)(a) op (JSInt64)(b)) #define JSLL_UCMP(a, op, b) ((JSUint64)(a) op (JSUint64)(b)) /*********************************************************************** ** MACROS: JSLL_ ** ** JSLL_AND Logical and ** JSLL_OR Logical or ** JSLL_XOR Logical exclusion ** JSLL_OR2 A disgusting deviation ** JSLL_NOT Negation (one's compliment) ***********************************************************************/ #define JSLL_AND(r, a, b) ((r) = (a) & (b)) #define JSLL_OR(r, a, b) ((r) = (a) | (b)) #define JSLL_XOR(r, a, b) ((r) = (a) ^ (b)) #define JSLL_OR2(r, a) ((r) = (r) | (a)) #define JSLL_NOT(r, a) ((r) = ~(a)) /*********************************************************************** ** MACROS: JSLL_ ** ** JSLL_NEG Negation (two's compliment) ** JSLL_ADD Summation (two's compliment) ** JSLL_SUB Difference (two's compliment) ***********************************************************************/ #define JSLL_NEG(r, a) ((r) = -(a)) #define JSLL_ADD(r, a, b) ((r) = (a) + (b)) #define JSLL_SUB(r, a, b) ((r) = (a) - (b)) /*********************************************************************** ** MACROS: JSLL_ ** ** JSLL_MUL Product (two's compliment) ** JSLL_DIV Quotient (two's compliment) ** JSLL_MOD Modulus (two's compliment) ***********************************************************************/ #define JSLL_MUL(r, a, b) ((r) = (a) * (b)) #define JSLL_DIV(r, a, b) ((r) = (a) / (b)) #define JSLL_MOD(r, a, b) ((r) = (a) % (b)) /*********************************************************************** ** MACROS: JSLL_ ** ** JSLL_SHL Shift left [0..64] bits ** JSLL_SHR Shift right [0..64] bits with sign extension ** JSLL_USHR Unsigned shift right [0..64] bits ** JSLL_ISHL Signed shift left [0..64] bits ***********************************************************************/ #define JSLL_SHL(r, a, b) ((r) = (JSInt64)(a) << (b)) #define JSLL_SHR(r, a, b) ((r) = (JSInt64)(a) >> (b)) #define JSLL_USHR(r, a, b) ((r) = (JSUint64)(a) >> (b)) #define JSLL_ISHL(r, a, b) ((r) = (JSInt64)(a) << (b)) /*********************************************************************** ** MACROS: JSLL_ ** ** JSLL_L2I Convert to signed 32 bit ** JSLL_L2UI Convert to unsigned 32 bit ** JSLL_L2F Convert to floating point ** JSLL_L2D Convert to floating point ** JSLL_I2L Convert signed to 64 bit ** JSLL_UI2L Convert unsigned to 64 bit ** JSLL_F2L Convert float to 64 bit ** JSLL_D2L Convert float to 64 bit ***********************************************************************/ #define JSLL_L2I(i, l) ((i) = (JSInt32)(l)) #define JSLL_L2UI(ui, l) ((ui) = (JSUint32)(l)) #define JSLL_L2F(f, l) ((f) = (JSFloat64)(l)) #define JSLL_L2D(d, l) ((d) = (JSFloat64)(l)) #define JSLL_I2L(l, i) ((l) = (JSInt64)(i)) #define JSLL_UI2L(l, ui) ((l) = (JSInt64)(ui)) #define JSLL_F2L(l, f) ((l) = (JSInt64)(f)) #define JSLL_D2L(l, d) ((l) = (JSInt64)(d)) /*********************************************************************** ** MACROS: JSLL_UDIVMOD ** DESCRIPTION: ** Produce both a quotient and a remainder given an unsigned ** INPUTS: JSUint64 a: The dividend of the operation ** JSUint64 b: The quotient of the operation ** OUTPUTS: JSUint64 *qp: pointer to quotient ** JSUint64 *rp: pointer to remainder ***********************************************************************/ #define JSLL_UDIVMOD(qp, rp, a, b) \ (*(qp) = ((JSUint64)(a) / (b)), \ *(rp) = ((JSUint64)(a) % (b))) #else /* !JS_HAVE_LONG_LONG */ #ifdef IS_LITTLE_ENDIAN #define JSLL_INIT(hi, lo) {JS_INT32(lo), JS_INT32(hi)} #else #define JSLL_INIT(hi, lo) {JS_INT32(hi), JS_INT32(lo)} #endif #define JSLL_IS_ZERO(a) (((a).hi == 0) && ((a).lo == 0)) #define JSLL_EQ(a, b) (((a).hi == (b).hi) && ((a).lo == (b).lo)) #define JSLL_NE(a, b) (((a).hi != (b).hi) || ((a).lo != (b).lo)) #define JSLL_GE_ZERO(a) (((a).hi >> 31) == 0) #ifdef DEBUG #define JSLL_CMP(a, op, b) (JS_ASSERT((#op)[1] != '='), JSLL_REAL_CMP(a, op, b)) #define JSLL_UCMP(a, op, b) (JS_ASSERT((#op)[1] != '='), JSLL_REAL_UCMP(a, op, b)) #else #define JSLL_CMP(a, op, b) JSLL_REAL_CMP(a, op, b) #define JSLL_UCMP(a, op, b) JSLL_REAL_UCMP(a, op, b) #endif #define JSLL_REAL_CMP(a,op,b) (((JSInt32)(a).hi op (JSInt32)(b).hi) || \ (((a).hi == (b).hi) && ((a).lo op (b).lo))) #define JSLL_REAL_UCMP(a,op,b) (((a).hi op (b).hi) || \ (((a).hi == (b).hi) && ((a).lo op (b).lo))) #define JSLL_AND(r, a, b) ((r).lo = (a).lo & (b).lo, \ (r).hi = (a).hi & (b).hi) #define JSLL_OR(r, a, b) ((r).lo = (a).lo | (b).lo, \ (r).hi = (a).hi | (b).hi) #define JSLL_XOR(r, a, b) ((r).lo = (a).lo ^ (b).lo, \ (r).hi = (a).hi ^ (b).hi) #define JSLL_OR2(r, a) ((r).lo = (r).lo | (a).lo, \ (r).hi = (r).hi | (a).hi) #define JSLL_NOT(r, a) ((r).lo = ~(a).lo, \ (r).hi = ~(a).hi) #define JSLL_NEG(r, a) ((r).lo = -(JSInt32)(a).lo, \ (r).hi = -(JSInt32)(a).hi - ((r).lo != 0)) #define JSLL_ADD(r, a, b) { \ JSInt64 _a, _b; \ _a = a; _b = b; \ (r).lo = _a.lo + _b.lo; \ (r).hi = _a.hi + _b.hi + ((r).lo < _b.lo); \ } #define JSLL_SUB(r, a, b) { \ JSInt64 _a, _b; \ _a = a; _b = b; \ (r).lo = _a.lo - _b.lo; \ (r).hi = _a.hi - _b.hi - (_a.lo < _b.lo); \ } #define JSLL_MUL(r, a, b) { \ JSInt64 _a, _b; \ _a = a; _b = b; \ JSLL_MUL32(r, _a.lo, _b.lo); \ (r).hi += _a.hi * _b.lo + _a.lo * _b.hi; \ } #define jslo16(a) ((a) & JS_BITMASK(16)) #define jshi16(a) ((a) >> 16) #define JSLL_MUL32(r, a, b) { \ JSUint32 _a1, _a0, _b1, _b0, _y0, _y1, _y2, _y3; \ _a1 = jshi16(a), _a0 = jslo16(a); \ _b1 = jshi16(b), _b0 = jslo16(b); \ _y0 = _a0 * _b0; \ _y1 = _a0 * _b1; \ _y2 = _a1 * _b0; \ _y3 = _a1 * _b1; \ _y1 += jshi16(_y0); /* can't carry */ \ _y1 += _y2; /* might carry */ \ if (_y1 < _y2) \ _y3 += (JSUint32)(JS_BIT(16)); /* propagate */ \ (r).lo = (jslo16(_y1) << 16) + jslo16(_y0); \ (r).hi = _y3 + jshi16(_y1); \ } #define JSLL_UDIVMOD(qp, rp, a, b) jsll_udivmod(qp, rp, a, b) extern JS_PUBLIC_API(void) jsll_udivmod(JSUint64 *qp, JSUint64 *rp, JSUint64 a, JSUint64 b); #define JSLL_DIV(r, a, b) { \ JSInt64 _a, _b; \ JSUint32 _negative = (JSInt32)(a).hi < 0; \ if (_negative) { \ JSLL_NEG(_a, a); \ } else { \ _a = a; \ } \ if ((JSInt32)(b).hi < 0) { \ _negative ^= 1; \ JSLL_NEG(_b, b); \ } else { \ _b = b; \ } \ JSLL_UDIVMOD(&(r), 0, _a, _b); \ if (_negative) \ JSLL_NEG(r, r); \ } #define JSLL_MOD(r, a, b) { \ JSInt64 _a, _b; \ JSUint32 _negative = (JSInt32)(a).hi < 0; \ if (_negative) { \ JSLL_NEG(_a, a); \ } else { \ _a = a; \ } \ if ((JSInt32)(b).hi < 0) { \ JSLL_NEG(_b, b); \ } else { \ _b = b; \ } \ JSLL_UDIVMOD(0, &(r), _a, _b); \ if (_negative) \ JSLL_NEG(r, r); \ } #define JSLL_SHL(r, a, b) { \ if (b) { \ JSInt64 _a; \ _a = a; \ if ((b) < 32) { \ (r).lo = _a.lo << ((b) & 31); \ (r).hi = (_a.hi << ((b) & 31)) | (_a.lo >> (32 - (b))); \ } else { \ (r).lo = 0; \ (r).hi = _a.lo << ((b) & 31); \ } \ } else { \ (r) = (a); \ } \ } /* a is an JSInt32, b is JSInt32, r is JSInt64 */ #define JSLL_ISHL(r, a, b) { \ if (b) { \ JSInt64 _a; \ _a.lo = (a); \ _a.hi = 0; \ if ((b) < 32) { \ (r).lo = (a) << ((b) & 31); \ (r).hi = ((a) >> (32 - (b))); \ } else { \ (r).lo = 0; \ (r).hi = (a) << ((b) & 31); \ } \ } else { \ (r).lo = (a); \ (r).hi = 0; \ } \ } #define JSLL_SHR(r, a, b) { \ if (b) { \ JSInt64 _a; \ _a = a; \ if ((b) < 32) { \ (r).lo = (_a.hi << (32 - (b))) | (_a.lo >> ((b) & 31)); \ (r).hi = (JSInt32)_a.hi >> ((b) & 31); \ } else { \ (r).lo = (JSInt32)_a.hi >> ((b) & 31); \ (r).hi = (JSInt32)_a.hi >> 31; \ } \ } else { \ (r) = (a); \ } \ } #define JSLL_USHR(r, a, b) { \ if (b) { \ JSInt64 _a; \ _a = a; \ if ((b) < 32) { \ (r).lo = (_a.hi << (32 - (b))) | (_a.lo >> ((b) & 31)); \ (r).hi = _a.hi >> ((b) & 31); \ } else { \ (r).lo = _a.hi >> ((b) & 31); \ (r).hi = 0; \ } \ } else { \ (r) = (a); \ } \ } #define JSLL_L2I(i, l) ((i) = (l).lo) #define JSLL_L2UI(ui, l) ((ui) = (l).lo) #define JSLL_L2F(f, l) { double _d; JSLL_L2D(_d, l); (f) = (JSFloat64)_d; } #define JSLL_L2D(d, l) { \ int _negative; \ JSInt64 _absval; \ \ _negative = (l).hi >> 31; \ if (_negative) { \ JSLL_NEG(_absval, l); \ } else { \ _absval = l; \ } \ (d) = (double)_absval.hi * 4.294967296e9 + _absval.lo; \ if (_negative) \ (d) = -(d); \ } #define JSLL_I2L(l, i) { JSInt32 _i = (i) >> 31; (l).lo = (i); (l).hi = _i; } #define JSLL_UI2L(l, ui) ((l).lo = (ui), (l).hi = 0) #define JSLL_F2L(l, f) { double _d = (double)f; JSLL_D2L(l, _d); } #define JSLL_D2L(l, d) { \ int _negative; \ double _absval, _d_hi; \ JSInt64 _lo_d; \ \ _negative = ((d) < 0); \ _absval = _negative ? -(d) : (d); \ \ (l).hi = _absval / 4.294967296e9; \ (l).lo = 0; \ JSLL_L2D(_d_hi, l); \ _absval -= _d_hi; \ _lo_d.hi = 0; \ if (_absval < 0) { \ _lo_d.lo = -_absval; \ JSLL_SUB(l, l, _lo_d); \ } else { \ _lo_d.lo = _absval; \ JSLL_ADD(l, l, _lo_d); \ } \ \ if (_negative) \ JSLL_NEG(l, l); \ } #endif /* !JS_HAVE_LONG_LONG */ JS_END_EXTERN_C #endif /* jslong_h___ */ pacparser-1.4.5/src/spidermonkey/js/src/jsmath.c000066400000000000000000000333171464010763600216540ustar00rootroot00000000000000/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- * * ***** BEGIN LICENSE BLOCK ***** * Version: MPL 1.1/GPL 2.0/LGPL 2.1 * * The contents of this file are subject to the Mozilla Public License Version * 1.1 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * http://www.mozilla.org/MPL/ * * Software distributed under the License is distributed on an "AS IS" basis, * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License * for the specific language governing rights and limitations under the * License. * * The Original Code is Mozilla Communicator client code, released * March 31, 1998. * * The Initial Developer of the Original Code is * Netscape Communications Corporation. * Portions created by the Initial Developer are Copyright (C) 1998 * the Initial Developer. All Rights Reserved. * * Contributor(s): * * Alternatively, the contents of this file may be used under the terms of * either of the GNU General Public License Version 2 or later (the "GPL"), * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), * in which case the provisions of the GPL or the LGPL are applicable instead * of those above. If you wish to allow use of your version of this file only * under the terms of either the GPL or the LGPL, and not to allow others to * use your version of this file under the terms of the MPL, indicate your * decision by deleting the provisions above and replace them with the notice * and other provisions required by the GPL or the LGPL. If you do not delete * the provisions above, a recipient may use your version of this file under * the terms of any one of the MPL, the GPL or the LGPL. * * ***** END LICENSE BLOCK ***** */ /* * JS math package. */ #include "jsstddef.h" #include "jslibmath.h" #include #include "jstypes.h" #include "jslong.h" #include "prmjtime.h" #include "jsapi.h" #include "jsatom.h" #include "jscntxt.h" #include "jsconfig.h" #include "jslock.h" #include "jsmath.h" #include "jsnum.h" #include "jsobj.h" #ifndef M_E #define M_E 2.7182818284590452354 #endif #ifndef M_LOG2E #define M_LOG2E 1.4426950408889634074 #endif #ifndef M_LOG10E #define M_LOG10E 0.43429448190325182765 #endif #ifndef M_LN2 #define M_LN2 0.69314718055994530942 #endif #ifndef M_LN10 #define M_LN10 2.30258509299404568402 #endif #ifndef M_PI #define M_PI 3.14159265358979323846 #endif #ifndef M_SQRT2 #define M_SQRT2 1.41421356237309504880 #endif #ifndef M_SQRT1_2 #define M_SQRT1_2 0.70710678118654752440 #endif static JSConstDoubleSpec math_constants[] = { {M_E, "E", 0, {0,0,0}}, {M_LOG2E, "LOG2E", 0, {0,0,0}}, {M_LOG10E, "LOG10E", 0, {0,0,0}}, {M_LN2, "LN2", 0, {0,0,0}}, {M_LN10, "LN10", 0, {0,0,0}}, {M_PI, "PI", 0, {0,0,0}}, {M_SQRT2, "SQRT2", 0, {0,0,0}}, {M_SQRT1_2, "SQRT1_2", 0, {0,0,0}}, {0,0,0,{0,0,0}} }; JSClass js_MathClass = { js_Math_str, JSCLASS_HAS_CACHED_PROTO(JSProto_Math), JS_PropertyStub, JS_PropertyStub, JS_PropertyStub, JS_PropertyStub, JS_EnumerateStub, JS_ResolveStub, JS_ConvertStub, JS_FinalizeStub, JSCLASS_NO_OPTIONAL_MEMBERS }; static JSBool math_abs(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) { jsdouble x, z; if (!js_ValueToNumber(cx, argv[0], &x)) return JS_FALSE; z = fd_fabs(x); return js_NewNumberValue(cx, z, rval); } static JSBool math_acos(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) { jsdouble x, z; if (!js_ValueToNumber(cx, argv[0], &x)) return JS_FALSE; z = fd_acos(x); return js_NewNumberValue(cx, z, rval); } static JSBool math_asin(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) { jsdouble x, z; if (!js_ValueToNumber(cx, argv[0], &x)) return JS_FALSE; z = fd_asin(x); return js_NewNumberValue(cx, z, rval); } static JSBool math_atan(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) { jsdouble x, z; if (!js_ValueToNumber(cx, argv[0], &x)) return JS_FALSE; z = fd_atan(x); return js_NewNumberValue(cx, z, rval); } static JSBool math_atan2(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) { jsdouble x, y, z; if (!js_ValueToNumber(cx, argv[0], &x)) return JS_FALSE; if (!js_ValueToNumber(cx, argv[1], &y)) return JS_FALSE; #if !JS_USE_FDLIBM_MATH && defined(_MSC_VER) /* * MSVC's atan2 does not yield the result demanded by ECMA when both x * and y are infinite. * - The result is a multiple of pi/4. * - The sign of x determines the sign of the result. * - The sign of y determines the multiplicator, 1 or 3. */ if (JSDOUBLE_IS_INFINITE(x) && JSDOUBLE_IS_INFINITE(y)) { z = fd_copysign(M_PI / 4, x); if (y < 0) z *= 3; return js_NewDoubleValue(cx, z, rval); } #endif z = fd_atan2(x, y); return js_NewNumberValue(cx, z, rval); } static JSBool math_ceil(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) { jsdouble x, z; if (!js_ValueToNumber(cx, argv[0], &x)) return JS_FALSE; z = fd_ceil(x); return js_NewNumberValue(cx, z, rval); } static JSBool math_cos(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) { jsdouble x, z; if (!js_ValueToNumber(cx, argv[0], &x)) return JS_FALSE; z = fd_cos(x); return js_NewNumberValue(cx, z, rval); } static JSBool math_exp(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) { jsdouble x, z; if (!js_ValueToNumber(cx, argv[0], &x)) return JS_FALSE; #ifdef _WIN32 if (!JSDOUBLE_IS_NaN(x)) { if (x == *cx->runtime->jsPositiveInfinity) { *rval = DOUBLE_TO_JSVAL(cx->runtime->jsPositiveInfinity); return JS_TRUE; } if (x == *cx->runtime->jsNegativeInfinity) { *rval = JSVAL_ZERO; return JS_TRUE; } } #endif z = fd_exp(x); return js_NewNumberValue(cx, z, rval); } static JSBool math_floor(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) { jsdouble x, z; if (!js_ValueToNumber(cx, argv[0], &x)) return JS_FALSE; z = fd_floor(x); return js_NewNumberValue(cx, z, rval); } static JSBool math_log(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) { jsdouble x, z; if (!js_ValueToNumber(cx, argv[0], &x)) return JS_FALSE; z = fd_log(x); return js_NewNumberValue(cx, z, rval); } static JSBool math_max(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) { jsdouble x, z = *cx->runtime->jsNegativeInfinity; uintN i; if (argc == 0) { *rval = DOUBLE_TO_JSVAL(cx->runtime->jsNegativeInfinity); return JS_TRUE; } for (i = 0; i < argc; i++) { if (!js_ValueToNumber(cx, argv[i], &x)) return JS_FALSE; if (JSDOUBLE_IS_NaN(x)) { *rval = DOUBLE_TO_JSVAL(cx->runtime->jsNaN); return JS_TRUE; } if (x == 0 && x == z && fd_copysign(1.0, z) == -1) z = x; else z = (x > z) ? x : z; } return js_NewNumberValue(cx, z, rval); } static JSBool math_min(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) { jsdouble x, z = *cx->runtime->jsPositiveInfinity; uintN i; if (argc == 0) { *rval = DOUBLE_TO_JSVAL(cx->runtime->jsPositiveInfinity); return JS_TRUE; } for (i = 0; i < argc; i++) { if (!js_ValueToNumber(cx, argv[i], &x)) return JS_FALSE; if (JSDOUBLE_IS_NaN(x)) { *rval = DOUBLE_TO_JSVAL(cx->runtime->jsNaN); return JS_TRUE; } if (x == 0 && x == z && fd_copysign(1.0,x) == -1) z = x; else z = (x < z) ? x : z; } return js_NewNumberValue(cx, z, rval); } static JSBool math_pow(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) { jsdouble x, y, z; if (!js_ValueToNumber(cx, argv[0], &x)) return JS_FALSE; if (!js_ValueToNumber(cx, argv[1], &y)) return JS_FALSE; #if !JS_USE_FDLIBM_MATH /* * Because C99 and ECMA specify different behavior for pow(), * we need to wrap the libm call to make it ECMA compliant. */ if (!JSDOUBLE_IS_FINITE(y) && (x == 1.0 || x == -1.0)) { *rval = DOUBLE_TO_JSVAL(cx->runtime->jsNaN); return JS_TRUE; } /* pow(x, +-0) is always 1, even for x = NaN. */ if (y == 0) { *rval = JSVAL_ONE; return JS_TRUE; } #endif z = fd_pow(x, y); return js_NewNumberValue(cx, z, rval); } /* * Math.random() support, lifted from java.util.Random.java. */ static void random_setSeed(JSRuntime *rt, int64 seed) { int64 tmp; JSLL_I2L(tmp, 1000); JSLL_DIV(seed, seed, tmp); JSLL_XOR(tmp, seed, rt->rngMultiplier); JSLL_AND(rt->rngSeed, tmp, rt->rngMask); } static void random_init(JSRuntime *rt) { int64 tmp, tmp2; /* Do at most once. */ if (rt->rngInitialized) return; rt->rngInitialized = JS_TRUE; /* rt->rngMultiplier = 0x5DEECE66DL */ JSLL_ISHL(tmp, 0x5, 32); JSLL_UI2L(tmp2, 0xDEECE66DL); JSLL_OR(rt->rngMultiplier, tmp, tmp2); /* rt->rngAddend = 0xBL */ JSLL_I2L(rt->rngAddend, 0xBL); /* rt->rngMask = (1L << 48) - 1 */ JSLL_I2L(tmp, 1); JSLL_SHL(tmp2, tmp, 48); JSLL_SUB(rt->rngMask, tmp2, tmp); /* rt->rngDscale = (jsdouble)(1L << 53) */ JSLL_SHL(tmp2, tmp, 53); JSLL_L2D(rt->rngDscale, tmp2); /* Finally, set the seed from current time. */ random_setSeed(rt, PRMJ_Now()); } static uint32 random_next(JSRuntime *rt, int bits) { int64 nextseed, tmp; uint32 retval; JSLL_MUL(nextseed, rt->rngSeed, rt->rngMultiplier); JSLL_ADD(nextseed, nextseed, rt->rngAddend); JSLL_AND(nextseed, nextseed, rt->rngMask); rt->rngSeed = nextseed; JSLL_USHR(tmp, nextseed, 48 - bits); JSLL_L2I(retval, tmp); return retval; } static jsdouble random_nextDouble(JSRuntime *rt) { int64 tmp, tmp2; jsdouble d; JSLL_ISHL(tmp, random_next(rt, 26), 27); JSLL_UI2L(tmp2, random_next(rt, 27)); JSLL_ADD(tmp, tmp, tmp2); JSLL_L2D(d, tmp); return d / rt->rngDscale; } static JSBool math_random(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) { JSRuntime *rt; jsdouble z; rt = cx->runtime; JS_LOCK_RUNTIME(rt); random_init(rt); z = random_nextDouble(rt); JS_UNLOCK_RUNTIME(rt); return js_NewNumberValue(cx, z, rval); } #if defined _WIN32 && !defined WINCE && _MSC_VER < 1400 /* Try to work around apparent _copysign bustage in VC6 and VC7. */ double js_copysign(double x, double y) { jsdpun xu, yu; xu.d = x; yu.d = y; xu.s.hi &= ~JSDOUBLE_HI32_SIGNBIT; xu.s.hi |= yu.s.hi & JSDOUBLE_HI32_SIGNBIT; return xu.d; } #endif static JSBool math_round(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) { jsdouble x, z; if (!js_ValueToNumber(cx, argv[0], &x)) return JS_FALSE; z = fd_copysign(fd_floor(x + 0.5), x); return js_NewNumberValue(cx, z, rval); } static JSBool math_sin(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) { jsdouble x, z; if (!js_ValueToNumber(cx, argv[0], &x)) return JS_FALSE; z = fd_sin(x); return js_NewNumberValue(cx, z, rval); } static JSBool math_sqrt(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) { jsdouble x, z; if (!js_ValueToNumber(cx, argv[0], &x)) return JS_FALSE; z = fd_sqrt(x); return js_NewNumberValue(cx, z, rval); } static JSBool math_tan(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) { jsdouble x, z; if (!js_ValueToNumber(cx, argv[0], &x)) return JS_FALSE; z = fd_tan(x); return js_NewNumberValue(cx, z, rval); } #if JS_HAS_TOSOURCE static JSBool math_toSource(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) { *rval = ATOM_KEY(CLASS_ATOM(cx, Math)); return JS_TRUE; } #endif static JSFunctionSpec math_static_methods[] = { #if JS_HAS_TOSOURCE {js_toSource_str, math_toSource, 0, 0, 0}, #endif {"abs", math_abs, 1, 0, 0}, {"acos", math_acos, 1, 0, 0}, {"asin", math_asin, 1, 0, 0}, {"atan", math_atan, 1, 0, 0}, {"atan2", math_atan2, 2, 0, 0}, {"ceil", math_ceil, 1, 0, 0}, {"cos", math_cos, 1, 0, 0}, {"exp", math_exp, 1, 0, 0}, {"floor", math_floor, 1, 0, 0}, {"log", math_log, 1, 0, 0}, {"max", math_max, 2, 0, 0}, {"min", math_min, 2, 0, 0}, {"pow", math_pow, 2, 0, 0}, {"random", math_random, 0, 0, 0}, {"round", math_round, 1, 0, 0}, {"sin", math_sin, 1, 0, 0}, {"sqrt", math_sqrt, 1, 0, 0}, {"tan", math_tan, 1, 0, 0}, {0,0,0,0,0} }; JSObject * js_InitMathClass(JSContext *cx, JSObject *obj) { JSObject *Math; Math = JS_DefineObject(cx, obj, js_Math_str, &js_MathClass, NULL, 0); if (!Math) return NULL; if (!JS_DefineFunctions(cx, Math, math_static_methods)) return NULL; if (!JS_DefineConstDoubles(cx, Math, math_constants)) return NULL; return Math; } pacparser-1.4.5/src/spidermonkey/js/src/jsmath.h000066400000000000000000000040671464010763600216610ustar00rootroot00000000000000/* ***** BEGIN LICENSE BLOCK ***** * Version: MPL 1.1/GPL 2.0/LGPL 2.1 * * The contents of this file are subject to the Mozilla Public License Version * 1.1 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * http://www.mozilla.org/MPL/ * * Software distributed under the License is distributed on an "AS IS" basis, * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License * for the specific language governing rights and limitations under the * License. * * The Original Code is Mozilla Communicator client code, released * March 31, 1998. * * The Initial Developer of the Original Code is * Netscape Communications Corporation. * Portions created by the Initial Developer are Copyright (C) 1998-1999 * the Initial Developer. All Rights Reserved. * * Contributor(s): * * Alternatively, the contents of this file may be used under the terms of * either of the GNU General Public License Version 2 or later (the "GPL"), * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), * in which case the provisions of the GPL or the LGPL are applicable instead * of those above. If you wish to allow use of your version of this file only * under the terms of either the GPL or the LGPL, and not to allow others to * use your version of this file under the terms of the MPL, indicate your * decision by deleting the provisions above and replace them with the notice * and other provisions required by the GPL or the LGPL. If you do not delete * the provisions above, a recipient may use your version of this file under * the terms of any one of the MPL, the GPL or the LGPL. * * ***** END LICENSE BLOCK ***** */ /* -*- Mode: C; tab-width: 8 -*- * Copyright (C) 1998-1999 Netscape Communications Corporation, All Rights Reserved. */ #ifndef jsmath_h___ #define jsmath_h___ /* * JS math functions. */ JS_BEGIN_EXTERN_C extern JSClass js_MathClass; extern JSObject * js_InitMathClass(JSContext *cx, JSObject *obj); JS_END_EXTERN_C #endif /* jsmath_h___ */ pacparser-1.4.5/src/spidermonkey/js/src/jsnum.c000066400000000000000000000776721464010763600215360ustar00rootroot00000000000000/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- * * ***** BEGIN LICENSE BLOCK ***** * Version: MPL 1.1/GPL 2.0/LGPL 2.1 * * The contents of this file are subject to the Mozilla Public License Version * 1.1 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * http://www.mozilla.org/MPL/ * * Software distributed under the License is distributed on an "AS IS" basis, * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License * for the specific language governing rights and limitations under the * License. * * The Original Code is Mozilla Communicator client code, released * March 31, 1998. * * The Initial Developer of the Original Code is * Netscape Communications Corporation. * Portions created by the Initial Developer are Copyright (C) 1998 * the Initial Developer. All Rights Reserved. * * Contributor(s): * IBM Corp. * * Alternatively, the contents of this file may be used under the terms of * either of the GNU General Public License Version 2 or later (the "GPL"), * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), * in which case the provisions of the GPL or the LGPL are applicable instead * of those above. If you wish to allow use of your version of this file only * under the terms of either the GPL or the LGPL, and not to allow others to * use your version of this file under the terms of the MPL, indicate your * decision by deleting the provisions above and replace them with the notice * and other provisions required by the GPL or the LGPL. If you do not delete * the provisions above, a recipient may use your version of this file under * the terms of any one of the MPL, the GPL or the LGPL. * * ***** END LICENSE BLOCK ***** */ /* * JS number type and wrapper class. */ #include "jsstddef.h" #if defined(XP_WIN) || defined(XP_OS2) #include #endif #include #include #include #include #include #include "jstypes.h" #include "jsutil.h" /* Added by JSIFY */ #include "jsapi.h" #include "jsatom.h" #include "jscntxt.h" #include "jsconfig.h" #include "jsdtoa.h" #include "jsgc.h" #include "jsinterp.h" #include "jsnum.h" #include "jsobj.h" #include "jsopcode.h" #include "jsprf.h" #include "jsstr.h" static JSBool num_isNaN(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) { jsdouble x; if (!js_ValueToNumber(cx, argv[0], &x)) return JS_FALSE; *rval = BOOLEAN_TO_JSVAL(JSDOUBLE_IS_NaN(x)); return JS_TRUE; } static JSBool num_isFinite(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) { jsdouble x; if (!js_ValueToNumber(cx, argv[0], &x)) return JS_FALSE; *rval = BOOLEAN_TO_JSVAL(JSDOUBLE_IS_FINITE(x)); return JS_TRUE; } static JSBool num_parseFloat(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) { JSString *str; jsdouble d; const jschar *bp, *ep; str = js_ValueToString(cx, argv[0]); if (!str) return JS_FALSE; /* XXXbe js_strtod shouldn't require NUL termination */ bp = js_UndependString(cx, str); if (!bp) return JS_FALSE; if (!js_strtod(cx, bp, &ep, &d)) return JS_FALSE; if (ep == bp) { *rval = DOUBLE_TO_JSVAL(cx->runtime->jsNaN); return JS_TRUE; } return js_NewNumberValue(cx, d, rval); } /* See ECMA 15.1.2.2. */ static JSBool num_parseInt(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) { jsint radix; JSString *str; jsdouble d; const jschar *bp, *ep; if (argc > 1) { if (!js_ValueToECMAInt32(cx, argv[1], &radix)) return JS_FALSE; } else { radix = 0; } if (radix != 0 && (radix < 2 || radix > 36)) { *rval = DOUBLE_TO_JSVAL(cx->runtime->jsNaN); return JS_TRUE; } str = js_ValueToString(cx, argv[0]); if (!str) return JS_FALSE; /* XXXbe js_strtointeger shouldn't require NUL termination */ bp = js_UndependString(cx, str); if (!bp) return JS_FALSE; if (!js_strtointeger(cx, bp, &ep, radix, &d)) return JS_FALSE; if (ep == bp) { *rval = DOUBLE_TO_JSVAL(cx->runtime->jsNaN); return JS_TRUE; } return js_NewNumberValue(cx, d, rval); } const char js_Infinity_str[] = "Infinity"; const char js_NaN_str[] = "NaN"; const char js_isNaN_str[] = "isNaN"; const char js_isFinite_str[] = "isFinite"; const char js_parseFloat_str[] = "parseFloat"; const char js_parseInt_str[] = "parseInt"; static JSFunctionSpec number_functions[] = { {js_isNaN_str, num_isNaN, 1,0,0}, {js_isFinite_str, num_isFinite, 1,0,0}, {js_parseFloat_str, num_parseFloat, 1,0,0}, {js_parseInt_str, num_parseInt, 2,0,0}, {0,0,0,0,0} }; JSClass js_NumberClass = { js_Number_str, JSCLASS_HAS_PRIVATE | JSCLASS_HAS_CACHED_PROTO(JSProto_Number), JS_PropertyStub, JS_PropertyStub, JS_PropertyStub, JS_PropertyStub, JS_EnumerateStub, JS_ResolveStub, JS_ConvertStub, JS_FinalizeStub, JSCLASS_NO_OPTIONAL_MEMBERS }; static JSBool Number(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) { jsdouble d; jsval v; if (argc != 0) { if (!js_ValueToNumber(cx, argv[0], &d)) return JS_FALSE; } else { d = 0.0; } if (!js_NewNumberValue(cx, d, &v)) return JS_FALSE; if (!(cx->fp->flags & JSFRAME_CONSTRUCTING)) { *rval = v; return JS_TRUE; } OBJ_SET_SLOT(cx, obj, JSSLOT_PRIVATE, v); return JS_TRUE; } #if JS_HAS_TOSOURCE static JSBool num_toSource(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) { jsval v; jsdouble d; char numBuf[DTOSTR_STANDARD_BUFFER_SIZE], *numStr; char buf[64]; JSString *str; if (JSVAL_IS_NUMBER((jsval)obj)) { v = (jsval)obj; } else { if (!JS_InstanceOf(cx, obj, &js_NumberClass, argv)) return JS_FALSE; v = OBJ_GET_SLOT(cx, obj, JSSLOT_PRIVATE); JS_ASSERT(JSVAL_IS_NUMBER(v)); } d = JSVAL_IS_INT(v) ? (jsdouble)JSVAL_TO_INT(v) : *JSVAL_TO_DOUBLE(v); numStr = JS_dtostr(numBuf, sizeof numBuf, DTOSTR_STANDARD, 0, d); if (!numStr) { JS_ReportOutOfMemory(cx); return JS_FALSE; } JS_snprintf(buf, sizeof buf, "(new %s(%s))", js_NumberClass.name, numStr); str = JS_NewStringCopyZ(cx, buf); if (!str) return JS_FALSE; *rval = STRING_TO_JSVAL(str); return JS_TRUE; } #endif /* The buf must be big enough for MIN_INT to fit including '-' and '\0'. */ static char * IntToString(jsint i, char *buf, size_t bufSize) { char *cp; jsuint u; u = (i < 0) ? -i : i; cp = buf + bufSize; /* one past last buffer cell */ *--cp = '\0'; /* null terminate the string to be */ /* * Build the string from behind. We use multiply and subtraction * instead of modulus because that's much faster. */ do { jsuint newu = u / 10; *--cp = (char)(u - newu * 10) + '0'; u = newu; } while (u != 0); if (i < 0) *--cp = '-'; return cp; } static JSBool num_toString(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) { jsval v; jsdouble d; jsint base; JSString *str; if (JSVAL_IS_NUMBER((jsval)obj)) { v = (jsval)obj; } else { if (!JS_InstanceOf(cx, obj, &js_NumberClass, argv)) return JS_FALSE; v = OBJ_GET_SLOT(cx, obj, JSSLOT_PRIVATE); JS_ASSERT(JSVAL_IS_NUMBER(v)); } d = JSVAL_IS_INT(v) ? (jsdouble)JSVAL_TO_INT(v) : *JSVAL_TO_DOUBLE(v); base = 10; if (argc != 0) { if (!js_ValueToECMAInt32(cx, argv[0], &base)) return JS_FALSE; if (base < 2 || base > 36) { char numBuf[12]; char *numStr = IntToString(base, numBuf, sizeof numBuf); JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_BAD_RADIX, numStr); return JS_FALSE; } } if (base == 10) { str = js_NumberToString(cx, d); } else { char *dStr = JS_dtobasestr(base, d); if (!dStr) { JS_ReportOutOfMemory(cx); return JS_FALSE; } str = JS_NewStringCopyZ(cx, dStr); free(dStr); } if (!str) return JS_FALSE; *rval = STRING_TO_JSVAL(str); return JS_TRUE; } static JSBool num_toLocaleString(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) { char thousandsLength, decimalLength; const char *numGrouping, *tmpGroup; JSRuntime *rt; JSString *numStr, *str; char *num, *buf, *dec, *end, *tmpSrc, *tmpDest; int digits, size, remainder, nrepeat; /* * Create the string, move back to bytes to make string twiddling * a bit easier and so we can insert platform charset seperators. */ if (!num_toString(cx, obj, 0, argv, rval)) return JS_FALSE; JS_ASSERT(JSVAL_IS_STRING(*rval)); numStr = JSVAL_TO_STRING(*rval); num = js_GetStringBytes(cx->runtime, numStr); /* Find bit before the decimal. */ dec = strchr(num, '.'); digits = dec ? dec - num : (int)strlen(num); end = num + digits; rt = cx->runtime; thousandsLength = strlen(rt->thousandsSeparator); decimalLength = strlen(rt->decimalSeparator); /* Figure out how long resulting string will be. */ size = digits + (dec ? decimalLength + strlen(dec + 1) : 0); numGrouping = tmpGroup = rt->numGrouping; remainder = digits; if (*num == '-') remainder--; while (*tmpGroup != CHAR_MAX && *tmpGroup != '\0') { if (*tmpGroup >= remainder) break; size += thousandsLength; remainder -= *tmpGroup; tmpGroup++; } if (*tmpGroup == '\0' && *numGrouping != '\0') { nrepeat = (remainder - 1) / tmpGroup[-1]; size += thousandsLength * nrepeat; remainder -= nrepeat * tmpGroup[-1]; } else { nrepeat = 0; } tmpGroup--; buf = (char *)JS_malloc(cx, size + 1); if (!buf) return JS_FALSE; tmpDest = buf; tmpSrc = num; while (*tmpSrc == '-' || remainder--) *tmpDest++ = *tmpSrc++; while (tmpSrc < end) { strcpy(tmpDest, rt->thousandsSeparator); tmpDest += thousandsLength; memcpy(tmpDest, tmpSrc, *tmpGroup); tmpDest += *tmpGroup; tmpSrc += *tmpGroup; if (--nrepeat < 0) tmpGroup--; } if (dec) { strcpy(tmpDest, rt->decimalSeparator); tmpDest += decimalLength; strcpy(tmpDest, dec + 1); } else { *tmpDest++ = '\0'; } if (cx->localeCallbacks && cx->localeCallbacks->localeToUnicode) return cx->localeCallbacks->localeToUnicode(cx, buf, rval); str = JS_NewString(cx, buf, size); if (!str) { JS_free(cx, buf); return JS_FALSE; } *rval = STRING_TO_JSVAL(str); return JS_TRUE; } static JSBool num_valueOf(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) { if (JSVAL_IS_NUMBER((jsval)obj)) { *rval = (jsval)obj; return JS_TRUE; } if (!JS_InstanceOf(cx, obj, &js_NumberClass, argv)) return JS_FALSE; *rval = OBJ_GET_SLOT(cx, obj, JSSLOT_PRIVATE); return JS_TRUE; } #define MAX_PRECISION 100 static JSBool num_to(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval, JSDToStrMode zeroArgMode, JSDToStrMode oneArgMode, jsint precisionMin, jsint precisionMax, jsint precisionOffset) { jsval v; jsdouble d, precision; JSString *str; char buf[DTOSTR_VARIABLE_BUFFER_SIZE(MAX_PRECISION+1)], *numStr; /* Use MAX_PRECISION+1 because precisionOffset can be 1 */ if (JSVAL_IS_NUMBER((jsval)obj)) { v = (jsval)obj; } else { if (!JS_InstanceOf(cx, obj, &js_NumberClass, argv)) return JS_FALSE; v = OBJ_GET_SLOT(cx, obj, JSSLOT_PRIVATE); JS_ASSERT(JSVAL_IS_NUMBER(v)); } d = JSVAL_IS_INT(v) ? (jsdouble)JSVAL_TO_INT(v) : *JSVAL_TO_DOUBLE(v); if (JSVAL_IS_VOID(argv[0])) { precision = 0.0; oneArgMode = zeroArgMode; } else { if (!js_ValueToNumber(cx, argv[0], &precision)) return JS_FALSE; precision = js_DoubleToInteger(precision); if (precision < precisionMin || precision > precisionMax) { numStr = JS_dtostr(buf, sizeof buf, DTOSTR_STANDARD, 0, precision); if (!numStr) JS_ReportOutOfMemory(cx); else JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_PRECISION_RANGE, numStr); return JS_FALSE; } } numStr = JS_dtostr(buf, sizeof buf, oneArgMode, (jsint)precision + precisionOffset, d); if (!numStr) { JS_ReportOutOfMemory(cx); return JS_FALSE; } str = JS_NewStringCopyZ(cx, numStr); if (!str) return JS_FALSE; *rval = STRING_TO_JSVAL(str); return JS_TRUE; } static JSBool num_toFixed(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) { /* We allow a larger range of precision than ECMA requires; this is permitted by ECMA. */ return num_to(cx, obj, argc, argv, rval, DTOSTR_FIXED, DTOSTR_FIXED, -20, MAX_PRECISION, 0); } static JSBool num_toExponential(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) { /* We allow a larger range of precision than ECMA requires; this is permitted by ECMA. */ return num_to(cx, obj, argc, argv, rval, DTOSTR_STANDARD_EXPONENTIAL, DTOSTR_EXPONENTIAL, 0, MAX_PRECISION, 1); } static JSBool num_toPrecision(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) { /* We allow a larger range of precision than ECMA requires; this is permitted by ECMA. */ return num_to(cx, obj, argc, argv, rval, DTOSTR_STANDARD, DTOSTR_PRECISION, 1, MAX_PRECISION, 0); } static JSFunctionSpec number_methods[] = { #if JS_HAS_TOSOURCE {js_toSource_str, num_toSource, 0,JSFUN_THISP_NUMBER,0}, #endif {js_toString_str, num_toString, 0,JSFUN_THISP_NUMBER,0}, {js_toLocaleString_str, num_toLocaleString, 0,JSFUN_THISP_NUMBER,0}, {js_valueOf_str, num_valueOf, 0,JSFUN_THISP_NUMBER,0}, {"toFixed", num_toFixed, 1,JSFUN_THISP_NUMBER,0}, {"toExponential", num_toExponential, 1,JSFUN_THISP_NUMBER,0}, {"toPrecision", num_toPrecision, 1,JSFUN_THISP_NUMBER,0}, {0,0,0,0,0} }; /* NB: Keep this in synch with number_constants[]. */ enum nc_slot { NC_NaN, NC_POSITIVE_INFINITY, NC_NEGATIVE_INFINITY, NC_MAX_VALUE, NC_MIN_VALUE, NC_LIMIT }; /* * Some to most C compilers forbid spelling these at compile time, or barf * if you try, so all but MAX_VALUE are set up by js_InitRuntimeNumberState * using union jsdpun. */ static JSConstDoubleSpec number_constants[] = { {0, js_NaN_str, 0,{0,0,0}}, {0, "POSITIVE_INFINITY", 0,{0,0,0}}, {0, "NEGATIVE_INFINITY", 0,{0,0,0}}, {1.7976931348623157E+308, "MAX_VALUE", 0,{0,0,0}}, {0, "MIN_VALUE", 0,{0,0,0}}, {0,0,0,{0,0,0}} }; static jsdouble NaN; #if (defined XP_WIN || defined XP_OS2) && \ !defined WINCE && \ !defined __MWERKS__ && \ (defined _M_IX86 || \ (defined __GNUC__ && !defined __MINGW32__)) /* * Set the exception mask to mask all exceptions and set the FPU precision * to 53 bit mantissa. * On Alpha platform this is handled via Compiler option. */ #define FIX_FPU() _control87(_MCW_EM | _PC_53, _MCW_EM | _MCW_PC) #else #define FIX_FPU() ((void)0) #endif JSBool js_InitRuntimeNumberState(JSContext *cx) { JSRuntime *rt; jsdpun u; struct lconv *locale; rt = cx->runtime; JS_ASSERT(!rt->jsNaN); FIX_FPU(); u.s.hi = JSDOUBLE_HI32_EXPMASK | JSDOUBLE_HI32_MANTMASK; u.s.lo = 0xffffffff; number_constants[NC_NaN].dval = NaN = u.d; rt->jsNaN = js_NewDouble(cx, NaN, GCF_LOCK); if (!rt->jsNaN) return JS_FALSE; u.s.hi = JSDOUBLE_HI32_EXPMASK; u.s.lo = 0x00000000; number_constants[NC_POSITIVE_INFINITY].dval = u.d; rt->jsPositiveInfinity = js_NewDouble(cx, u.d, GCF_LOCK); if (!rt->jsPositiveInfinity) return JS_FALSE; u.s.hi = JSDOUBLE_HI32_SIGNBIT | JSDOUBLE_HI32_EXPMASK; u.s.lo = 0x00000000; number_constants[NC_NEGATIVE_INFINITY].dval = u.d; rt->jsNegativeInfinity = js_NewDouble(cx, u.d, GCF_LOCK); if (!rt->jsNegativeInfinity) return JS_FALSE; u.s.hi = 0; u.s.lo = 1; number_constants[NC_MIN_VALUE].dval = u.d; locale = localeconv(); rt->thousandsSeparator = JS_strdup(cx, locale->thousands_sep ? locale->thousands_sep : "'"); rt->decimalSeparator = JS_strdup(cx, locale->decimal_point ? locale->decimal_point : "."); rt->numGrouping = JS_strdup(cx, locale->grouping ? locale->grouping : "\3\0"); return rt->thousandsSeparator && rt->decimalSeparator && rt->numGrouping; } void js_FinishRuntimeNumberState(JSContext *cx) { JSRuntime *rt = cx->runtime; js_UnlockGCThingRT(rt, rt->jsNaN); js_UnlockGCThingRT(rt, rt->jsNegativeInfinity); js_UnlockGCThingRT(rt, rt->jsPositiveInfinity); rt->jsNaN = NULL; rt->jsNegativeInfinity = NULL; rt->jsPositiveInfinity = NULL; JS_free(cx, (void *)rt->thousandsSeparator); JS_free(cx, (void *)rt->decimalSeparator); JS_free(cx, (void *)rt->numGrouping); rt->thousandsSeparator = rt->decimalSeparator = rt->numGrouping = NULL; } JSObject * js_InitNumberClass(JSContext *cx, JSObject *obj) { JSObject *proto, *ctor; JSRuntime *rt; /* XXX must do at least once per new thread, so do it per JSContext... */ FIX_FPU(); if (!JS_DefineFunctions(cx, obj, number_functions)) return NULL; proto = JS_InitClass(cx, obj, NULL, &js_NumberClass, Number, 1, NULL, number_methods, NULL, NULL); if (!proto || !(ctor = JS_GetConstructor(cx, proto))) return NULL; OBJ_SET_SLOT(cx, proto, JSSLOT_PRIVATE, JSVAL_ZERO); if (!JS_DefineConstDoubles(cx, ctor, number_constants)) return NULL; /* ECMA 15.1.1.1 */ rt = cx->runtime; if (!JS_DefineProperty(cx, obj, js_NaN_str, DOUBLE_TO_JSVAL(rt->jsNaN), NULL, NULL, JSPROP_PERMANENT)) { return NULL; } /* ECMA 15.1.1.2 */ if (!JS_DefineProperty(cx, obj, js_Infinity_str, DOUBLE_TO_JSVAL(rt->jsPositiveInfinity), NULL, NULL, JSPROP_PERMANENT)) { return NULL; } return proto; } jsdouble * js_NewDouble(JSContext *cx, jsdouble d, uintN gcflag) { jsdouble *dp; dp = (jsdouble *) js_NewGCThing(cx, gcflag | GCX_DOUBLE, sizeof(jsdouble)); if (!dp) return NULL; *dp = d; return dp; } void js_FinalizeDouble(JSContext *cx, jsdouble *dp) { *dp = NaN; } JSBool js_NewDoubleValue(JSContext *cx, jsdouble d, jsval *rval) { jsdouble *dp; dp = js_NewDouble(cx, d, 0); if (!dp) return JS_FALSE; *rval = DOUBLE_TO_JSVAL(dp); return JS_TRUE; } JSBool js_NewNumberValue(JSContext *cx, jsdouble d, jsval *rval) { jsint i; if (JSDOUBLE_IS_INT(d, i) && INT_FITS_IN_JSVAL(i)) { *rval = INT_TO_JSVAL(i); } else { if (!js_NewDoubleValue(cx, d, rval)) return JS_FALSE; } return JS_TRUE; } JSObject * js_NumberToObject(JSContext *cx, jsdouble d) { JSObject *obj; jsval v; obj = js_NewObject(cx, &js_NumberClass, NULL, NULL); if (!obj) return NULL; if (!js_NewNumberValue(cx, d, &v)) { cx->weakRoots.newborn[GCX_OBJECT] = NULL; return NULL; } OBJ_SET_SLOT(cx, obj, JSSLOT_PRIVATE, v); return obj; } JSString * js_NumberToString(JSContext *cx, jsdouble d) { jsint i; char buf[DTOSTR_STANDARD_BUFFER_SIZE]; char *numStr; if (JSDOUBLE_IS_INT(d, i)) { numStr = IntToString(i, buf, sizeof buf); } else { numStr = JS_dtostr(buf, sizeof buf, DTOSTR_STANDARD, 0, d); if (!numStr) { JS_ReportOutOfMemory(cx); return NULL; } } return JS_NewStringCopyZ(cx, numStr); } JSBool js_ValueToNumber(JSContext *cx, jsval v, jsdouble *dp) { JSObject *obj; JSString *str; const jschar *bp, *ep; if (JSVAL_IS_OBJECT(v)) { obj = JSVAL_TO_OBJECT(v); if (!obj) { *dp = 0; return JS_TRUE; } if (!OBJ_DEFAULT_VALUE(cx, obj, JSTYPE_NUMBER, &v)) return JS_FALSE; } if (JSVAL_IS_INT(v)) { *dp = (jsdouble)JSVAL_TO_INT(v); } else if (JSVAL_IS_DOUBLE(v)) { *dp = *JSVAL_TO_DOUBLE(v); } else if (JSVAL_IS_STRING(v)) { str = JSVAL_TO_STRING(v); /* * Note that ECMA doesn't treat a string beginning with a '0' as an * octal number here. This works because all such numbers will be * interpreted as decimal by js_strtod and will never get passed to * js_strtointeger (which would interpret them as octal). */ /* XXXbe js_strtod shouldn't require NUL termination */ bp = js_UndependString(cx, str); if (!bp) return JS_FALSE; if ((!js_strtod(cx, bp, &ep, dp) || js_SkipWhiteSpace(ep) != bp + str->length) && (!js_strtointeger(cx, bp, &ep, 0, dp) || js_SkipWhiteSpace(ep) != bp + str->length)) { goto badstr; } } else if (JSVAL_IS_BOOLEAN(v)) { *dp = JSVAL_TO_BOOLEAN(v) ? 1 : 0; } else { badstr: *dp = *cx->runtime->jsNaN; } return JS_TRUE; } JSBool js_ValueToECMAInt32(JSContext *cx, jsval v, int32 *ip) { jsdouble d; if (!js_ValueToNumber(cx, v, &d)) return JS_FALSE; return js_DoubleToECMAInt32(cx, d, ip); } JSBool js_DoubleToECMAInt32(JSContext *cx, jsdouble d, int32 *ip) { jsdouble two32 = 4294967296.0; jsdouble two31 = 2147483648.0; if (!JSDOUBLE_IS_FINITE(d) || d == 0) { *ip = 0; return JS_TRUE; } d = fmod(d, two32); d = (d >= 0) ? floor(d) : ceil(d) + two32; if (d >= two31) *ip = (int32)(d - two32); else *ip = (int32)d; return JS_TRUE; } JSBool js_ValueToECMAUint32(JSContext *cx, jsval v, uint32 *ip) { jsdouble d; if (!js_ValueToNumber(cx, v, &d)) return JS_FALSE; return js_DoubleToECMAUint32(cx, d, ip); } JSBool js_DoubleToECMAUint32(JSContext *cx, jsdouble d, uint32 *ip) { JSBool neg; jsdouble two32 = 4294967296.0; if (!JSDOUBLE_IS_FINITE(d) || d == 0) { *ip = 0; return JS_TRUE; } neg = (d < 0); d = floor(neg ? -d : d); d = neg ? -d : d; d = fmod(d, two32); d = (d >= 0) ? d : d + two32; *ip = (uint32)d; return JS_TRUE; } JSBool js_ValueToInt32(JSContext *cx, jsval v, int32 *ip) { jsdouble d; JSString *str; if (JSVAL_IS_INT(v)) { *ip = JSVAL_TO_INT(v); return JS_TRUE; } if (!js_ValueToNumber(cx, v, &d)) return JS_FALSE; if (JSDOUBLE_IS_NaN(d) || d <= -2147483649.0 || 2147483648.0 <= d) { str = js_DecompileValueGenerator(cx, JSDVG_SEARCH_STACK, v, NULL); if (str) { JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_CANT_CONVERT, JS_GetStringBytes(str)); } return JS_FALSE; } *ip = (int32)floor(d + 0.5); /* Round to nearest */ return JS_TRUE; } JSBool js_ValueToUint16(JSContext *cx, jsval v, uint16 *ip) { jsdouble d; jsuint i, m; JSBool neg; if (!js_ValueToNumber(cx, v, &d)) return JS_FALSE; if (d == 0 || !JSDOUBLE_IS_FINITE(d)) { *ip = 0; return JS_TRUE; } i = (jsuint)d; if ((jsdouble)i == d) { *ip = (uint16)i; return JS_TRUE; } neg = (d < 0); d = floor(neg ? -d : d); d = neg ? -d : d; m = JS_BIT(16); d = fmod(d, (double)m); if (d < 0) d += m; *ip = (uint16) d; return JS_TRUE; } jsdouble js_DoubleToInteger(jsdouble d) { JSBool neg; if (d == 0) return d; if (!JSDOUBLE_IS_FINITE(d)) { if (JSDOUBLE_IS_NaN(d)) return 0; return d; } neg = (d < 0); d = floor(neg ? -d : d); return neg ? -d : d; } JSBool js_strtod(JSContext *cx, const jschar *s, const jschar **ep, jsdouble *dp) { char cbuf[32]; size_t i; char *cstr, *istr, *estr; JSBool negative; jsdouble d; const jschar *s1 = js_SkipWhiteSpace(s); size_t length = js_strlen(s1); /* Use cbuf to avoid malloc */ if (length >= sizeof cbuf) { cstr = (char *) JS_malloc(cx, length + 1); if (!cstr) return JS_FALSE; } else { cstr = cbuf; } for (i = 0; i <= length; i++) { if (s1[i] >> 8) { cstr[i] = 0; break; } cstr[i] = (char)s1[i]; } istr = cstr; if ((negative = (*istr == '-')) != 0 || *istr == '+') istr++; if (!strncmp(istr, js_Infinity_str, sizeof js_Infinity_str - 1)) { d = *(negative ? cx->runtime->jsNegativeInfinity : cx->runtime->jsPositiveInfinity); estr = istr + 8; } else { int err; d = JS_strtod(cstr, &estr, &err); if (err == JS_DTOA_ENOMEM) { JS_ReportOutOfMemory(cx); if (cstr != cbuf) JS_free(cx, cstr); return JS_FALSE; } if (err == JS_DTOA_ERANGE) { if (d == HUGE_VAL) d = *cx->runtime->jsPositiveInfinity; else if (d == -HUGE_VAL) d = *cx->runtime->jsNegativeInfinity; } #ifdef HPUX if (d == 0.0 && negative) { /* * "-0", "-1e-2000" come out as positive zero * here on HPUX. Force a negative zero instead. */ JSDOUBLE_HI32(d) = JSDOUBLE_HI32_SIGNBIT; JSDOUBLE_LO32(d) = 0; } #endif } i = estr - cstr; if (cstr != cbuf) JS_free(cx, cstr); *ep = i ? s1 + i : s; *dp = d; return JS_TRUE; } struct BinaryDigitReader { uintN base; /* Base of number; must be a power of 2 */ uintN digit; /* Current digit value in radix given by base */ uintN digitMask; /* Mask to extract the next bit from digit */ const jschar *digits; /* Pointer to the remaining digits */ const jschar *end; /* Pointer to first non-digit */ }; /* Return the next binary digit from the number or -1 if done */ static intN GetNextBinaryDigit(struct BinaryDigitReader *bdr) { intN bit; if (bdr->digitMask == 0) { uintN c; if (bdr->digits == bdr->end) return -1; c = *bdr->digits++; if ('0' <= c && c <= '9') bdr->digit = c - '0'; else if ('a' <= c && c <= 'z') bdr->digit = c - 'a' + 10; else bdr->digit = c - 'A' + 10; bdr->digitMask = bdr->base >> 1; } bit = (bdr->digit & bdr->digitMask) != 0; bdr->digitMask >>= 1; return bit; } JSBool js_strtointeger(JSContext *cx, const jschar *s, const jschar **ep, jsint base, jsdouble *dp) { JSBool negative; jsdouble value; const jschar *start; const jschar *s1 = js_SkipWhiteSpace(s); if ((negative = (*s1 == '-')) != 0 || *s1 == '+') s1++; if (base == 0) { /* No base supplied, or some base that evaluated to 0. */ if (*s1 == '0') { /* It's either hex or octal; only increment char if str isn't '0' */ if (s1[1] == 'X' || s1[1] == 'x') { /* Hex */ s1 += 2; base = 16; } else { /* Octal */ base = 8; } } else { base = 10; /* Default to decimal. */ } } else if (base == 16 && *s1 == '0' && (s1[1] == 'X' || s1[1] == 'x')) { /* If base is 16, ignore hex prefix. */ s1 += 2; } /* * Done with the preliminaries; find some prefix of the string that's * a number in the given base. */ start = s1; /* Mark - if string is empty, we return NaN. */ value = 0.0; for (;;) { uintN digit; jschar c = *s1; if ('0' <= c && c <= '9') digit = c - '0'; else if ('a' <= c && c <= 'z') digit = c - 'a' + 10; else if ('A' <= c && c <= 'Z') digit = c - 'A' + 10; else break; if (digit >= (uintN)base) break; value = value * base + digit; s1++; } if (value >= 9007199254740992.0) { if (base == 10) { /* * If we're accumulating a decimal number and the number is >= * 2^53, then the result from the repeated multiply-add above may * be inaccurate. Call JS_strtod to get the correct answer. */ size_t i; size_t length = s1 - start; char *cstr = (char *) JS_malloc(cx, length + 1); char *estr; int err=0; if (!cstr) return JS_FALSE; for (i = 0; i != length; i++) cstr[i] = (char)start[i]; cstr[length] = 0; value = JS_strtod(cstr, &estr, &err); if (err == JS_DTOA_ENOMEM) { JS_ReportOutOfMemory(cx); JS_free(cx, cstr); return JS_FALSE; } if (err == JS_DTOA_ERANGE && value == HUGE_VAL) value = *cx->runtime->jsPositiveInfinity; JS_free(cx, cstr); } else if ((base & (base - 1)) == 0) { /* * The number may also be inaccurate for power-of-two bases. This * happens if the addition in value * base + digit causes a round- * down to an even least significant mantissa bit when the first * dropped bit is a one. If any of the following digits in the * number (which haven't been added in yet) are nonzero, then the * correct action would have been to round up instead of down. An * example occurs when reading the number 0x1000000000000081, which * rounds to 0x1000000000000000 instead of 0x1000000000000100. */ struct BinaryDigitReader bdr; intN bit, bit2; intN j; bdr.base = base; bdr.digitMask = 0; bdr.digits = start; bdr.end = s1; value = 0.0; /* Skip leading zeros. */ do { bit = GetNextBinaryDigit(&bdr); } while (bit == 0); if (bit == 1) { /* Gather the 53 significant bits (including the leading 1) */ value = 1.0; for (j = 52; j; j--) { bit = GetNextBinaryDigit(&bdr); if (bit < 0) goto done; value = value*2 + bit; } /* bit2 is the 54th bit (the first dropped from the mantissa) */ bit2 = GetNextBinaryDigit(&bdr); if (bit2 >= 0) { jsdouble factor = 2.0; intN sticky = 0; /* sticky is 1 if any bit beyond the 54th is 1 */ intN bit3; while ((bit3 = GetNextBinaryDigit(&bdr)) >= 0) { sticky |= bit3; factor *= 2; } value += bit2 & (bit | sticky); value *= factor; } done:; } } } /* We don't worry about inaccurate numbers for any other base. */ if (s1 == start) { *dp = 0.0; *ep = s; } else { *dp = negative ? -value : value; *ep = s1; } return JS_TRUE; } pacparser-1.4.5/src/spidermonkey/js/src/jsnum.h000066400000000000000000000223041464010763600215210ustar00rootroot00000000000000/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- * * ***** BEGIN LICENSE BLOCK ***** * Version: MPL 1.1/GPL 2.0/LGPL 2.1 * * The contents of this file are subject to the Mozilla Public License Version * 1.1 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * http://www.mozilla.org/MPL/ * * Software distributed under the License is distributed on an "AS IS" basis, * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License * for the specific language governing rights and limitations under the * License. * * The Original Code is Mozilla Communicator client code, released * March 31, 1998. * * The Initial Developer of the Original Code is * Netscape Communications Corporation. * Portions created by the Initial Developer are Copyright (C) 1998 * the Initial Developer. All Rights Reserved. * * Contributor(s): * * Alternatively, the contents of this file may be used under the terms of * either of the GNU General Public License Version 2 or later (the "GPL"), * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), * in which case the provisions of the GPL or the LGPL are applicable instead * of those above. If you wish to allow use of your version of this file only * under the terms of either the GPL or the LGPL, and not to allow others to * use your version of this file under the terms of the MPL, indicate your * decision by deleting the provisions above and replace them with the notice * and other provisions required by the GPL or the LGPL. If you do not delete * the provisions above, a recipient may use your version of this file under * the terms of any one of the MPL, the GPL or the LGPL. * * ***** END LICENSE BLOCK ***** */ #ifndef jsnum_h___ #define jsnum_h___ /* * JS number (IEEE double) interface. * * JS numbers are optimistically stored in the top 31 bits of 32-bit integers, * but floating point literals, results that overflow 31 bits, and division and * modulus operands and results require a 64-bit IEEE double. These are GC'ed * and pointed to by 32-bit jsvals on the stack and in object properties. * * When a JS number is treated as an object (followed by . or []), the runtime * wraps it with a JSObject whose valueOf method returns the unwrapped number. */ JS_BEGIN_EXTERN_C /* * Stefan Hanske reports: * ARM is a little endian architecture but 64 bit double words are stored * differently: the 32 bit words are in little endian byte order, the two words * are stored in big endian`s way. */ #if defined(__arm) || defined(__arm32__) || defined(__arm26__) || defined(__arm__) #define CPU_IS_ARM #endif typedef union jsdpun { struct { #if defined(IS_LITTLE_ENDIAN) && !defined(CPU_IS_ARM) uint32 lo, hi; #else uint32 hi, lo; #endif } s; jsdouble d; } jsdpun; #if (__GNUC__ == 2 && __GNUC_MINOR__ > 95) || __GNUC__ > 2 /* * This version of the macros is safe for the alias optimizations that gcc * does, but uses gcc-specific extensions. */ #define JSDOUBLE_HI32(x) (__extension__ ({ jsdpun u; u.d = (x); u.s.hi; })) #define JSDOUBLE_LO32(x) (__extension__ ({ jsdpun u; u.d = (x); u.s.lo; })) #define JSDOUBLE_SET_HI32(x, y) \ (__extension__ ({ jsdpun u; u.d = (x); u.s.hi = (y); (x) = u.d; })) #define JSDOUBLE_SET_LO32(x, y) \ (__extension__ ({ jsdpun u; u.d = (x); u.s.lo = (y); (x) = u.d; })) #else /* not or old GNUC */ /* * We don't know of any non-gcc compilers that perform alias optimization, * so this code should work. */ #if defined(IS_LITTLE_ENDIAN) && !defined(CPU_IS_ARM) #define JSDOUBLE_HI32(x) (((uint32 *)&(x))[1]) #define JSDOUBLE_LO32(x) (((uint32 *)&(x))[0]) #else #define JSDOUBLE_HI32(x) (((uint32 *)&(x))[0]) #define JSDOUBLE_LO32(x) (((uint32 *)&(x))[1]) #endif #define JSDOUBLE_SET_HI32(x, y) (JSDOUBLE_HI32(x)=(y)) #define JSDOUBLE_SET_LO32(x, y) (JSDOUBLE_LO32(x)=(y)) #endif /* not or old GNUC */ #define JSDOUBLE_HI32_SIGNBIT 0x80000000 #define JSDOUBLE_HI32_EXPMASK 0x7ff00000 #define JSDOUBLE_HI32_MANTMASK 0x000fffff #define JSDOUBLE_IS_NaN(x) \ ((JSDOUBLE_HI32(x) & JSDOUBLE_HI32_EXPMASK) == JSDOUBLE_HI32_EXPMASK && \ (JSDOUBLE_LO32(x) || (JSDOUBLE_HI32(x) & JSDOUBLE_HI32_MANTMASK))) #define JSDOUBLE_IS_INFINITE(x) \ ((JSDOUBLE_HI32(x) & ~JSDOUBLE_HI32_SIGNBIT) == JSDOUBLE_HI32_EXPMASK && \ !JSDOUBLE_LO32(x)) #define JSDOUBLE_IS_FINITE(x) \ ((JSDOUBLE_HI32(x) & JSDOUBLE_HI32_EXPMASK) != JSDOUBLE_HI32_EXPMASK) #define JSDOUBLE_IS_NEGZERO(d) (JSDOUBLE_HI32(d) == JSDOUBLE_HI32_SIGNBIT && \ JSDOUBLE_LO32(d) == 0) /* * JSDOUBLE_IS_INT first checks that d is neither NaN nor infinite, to avoid * raising SIGFPE on platforms such as Alpha Linux, then (only if the cast is * safe) leaves i as (jsint)d. This also avoid anomalous NaN floating point * comparisons under MSVC. */ #define JSDOUBLE_IS_INT(d, i) (JSDOUBLE_IS_FINITE(d) \ && !JSDOUBLE_IS_NEGZERO(d) \ && ((d) == (i = (jsint)(d)))) #if defined(XP_WIN) #define JSDOUBLE_COMPARE(LVAL, OP, RVAL, IFNAN) \ ((JSDOUBLE_IS_NaN(LVAL) || JSDOUBLE_IS_NaN(RVAL)) \ ? (IFNAN) \ : (LVAL) OP (RVAL)) #else #define JSDOUBLE_COMPARE(LVAL, OP, RVAL, IFNAN) ((LVAL) OP (RVAL)) #endif /* Initialize number constants and runtime state for the first context. */ extern JSBool js_InitRuntimeNumberState(JSContext *cx); extern void js_FinishRuntimeNumberState(JSContext *cx); /* Initialize the Number class, returning its prototype object. */ extern JSClass js_NumberClass; extern JSObject * js_InitNumberClass(JSContext *cx, JSObject *obj); /* * String constants for global function names, used in jsapi.c and jsnum.c. */ extern const char js_Infinity_str[]; extern const char js_NaN_str[]; extern const char js_isNaN_str[]; extern const char js_isFinite_str[]; extern const char js_parseFloat_str[]; extern const char js_parseInt_str[]; /* GC-allocate a new JS number. */ extern jsdouble * js_NewDouble(JSContext *cx, jsdouble d, uintN gcflag); extern void js_FinalizeDouble(JSContext *cx, jsdouble *dp); extern JSBool js_NewDoubleValue(JSContext *cx, jsdouble d, jsval *rval); extern JSBool js_NewNumberValue(JSContext *cx, jsdouble d, jsval *rval); /* Construct a Number instance that wraps around d. */ extern JSObject * js_NumberToObject(JSContext *cx, jsdouble d); /* Convert a number to a GC'ed string. */ extern JSString * js_NumberToString(JSContext *cx, jsdouble d); /* * Convert a value to a number, returning false after reporting any error, * otherwise returning true with *dp set. */ extern JSBool js_ValueToNumber(JSContext *cx, jsval v, jsdouble *dp); /* * Convert a value or a double to an int32, according to the ECMA rules * for ToInt32. */ extern JSBool js_ValueToECMAInt32(JSContext *cx, jsval v, int32 *ip); extern JSBool js_DoubleToECMAInt32(JSContext *cx, jsdouble d, int32 *ip); /* * Convert a value or a double to a uint32, according to the ECMA rules * for ToUint32. */ extern JSBool js_ValueToECMAUint32(JSContext *cx, jsval v, uint32 *ip); extern JSBool js_DoubleToECMAUint32(JSContext *cx, jsdouble d, uint32 *ip); /* * Convert a value to a number, then to an int32 if it fits by rounding to * nearest; but failing with an error report if the double is out of range * or unordered. */ extern JSBool js_ValueToInt32(JSContext *cx, jsval v, int32 *ip); /* * Convert a value to a number, then to a uint16 according to the ECMA rules * for ToUint16. */ extern JSBool js_ValueToUint16(JSContext *cx, jsval v, uint16 *ip); /* * Convert a jsdouble to an integral number, stored in a jsdouble. * If d is NaN, return 0. If d is an infinity, return it without conversion. */ extern jsdouble js_DoubleToInteger(jsdouble d); /* * Similar to strtod except that it replaces overflows with infinities of the * correct sign, and underflows with zeros of the correct sign. Guaranteed to * return the closest double number to the given input in dp. * * Also allows inputs of the form [+|-]Infinity, which produce an infinity of * the appropriate sign. The case of the "Infinity" string must match exactly. * If the string does not contain a number, set *ep to s and return 0.0 in dp. * Return false if out of memory. */ extern JSBool js_strtod(JSContext *cx, const jschar *s, const jschar **ep, jsdouble *dp); /* * Similar to strtol except that it handles integers of arbitrary size. * Guaranteed to return the closest double number to the given input when radix * is 10 or a power of 2. Callers may see round-off errors for very large * numbers of a different radix than 10 or a power of 2. * * If the string does not contain a number, set *ep to s and return 0.0 in dp. * Return false if out of memory. */ extern JSBool js_strtointeger(JSContext *cx, const jschar *s, const jschar **ep, jsint radix, jsdouble *dp); JS_END_EXTERN_C #endif /* jsnum_h___ */ pacparser-1.4.5/src/spidermonkey/js/src/jsobj.c000066400000000000000000004672721464010763600215100ustar00rootroot00000000000000/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- * vim: set ts=8 sw=4 et tw=78: * * ***** BEGIN LICENSE BLOCK ***** * Version: MPL 1.1/GPL 2.0/LGPL 2.1 * * The contents of this file are subject to the Mozilla Public License Version * 1.1 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * http://www.mozilla.org/MPL/ * * Software distributed under the License is distributed on an "AS IS" basis, * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License * for the specific language governing rights and limitations under the * License. * * The Original Code is Mozilla Communicator client code, released * March 31, 1998. * * The Initial Developer of the Original Code is * Netscape Communications Corporation. * Portions created by the Initial Developer are Copyright (C) 1998 * the Initial Developer. All Rights Reserved. * * Contributor(s): * * Alternatively, the contents of this file may be used under the terms of * either of the GNU General Public License Version 2 or later (the "GPL"), * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), * in which case the provisions of the GPL or the LGPL are applicable instead * of those above. If you wish to allow use of your version of this file only * under the terms of either the GPL or the LGPL, and not to allow others to * use your version of this file under the terms of the MPL, indicate your * decision by deleting the provisions above and replace them with the notice * and other provisions required by the GPL or the LGPL. If you do not delete * the provisions above, a recipient may use your version of this file under * the terms of any one of the MPL, the GPL or the LGPL. * * ***** END LICENSE BLOCK ***** */ /* * JS object implementation. */ #include "jsstddef.h" #include #include #include "jstypes.h" #include "jsarena.h" /* Added by JSIFY */ #include "jsbit.h" #include "jsutil.h" /* Added by JSIFY */ #include "jshash.h" /* Added by JSIFY */ #include "jsdhash.h" #include "jsprf.h" #include "jsapi.h" #include "jsarray.h" #include "jsatom.h" #include "jsbool.h" #include "jscntxt.h" #include "jsconfig.h" #include "jsfun.h" #include "jsgc.h" #include "jsinterp.h" #include "jslock.h" #include "jsnum.h" #include "jsobj.h" #include "jsscan.h" #include "jsscope.h" #include "jsscript.h" #include "jsstr.h" #include "jsopcode.h" #include "jsdbgapi.h" /* whether or not JS_HAS_OBJ_WATCHPOINT */ #if JS_HAS_GENERATORS #include "jsiter.h" #endif #if JS_HAS_XML_SUPPORT #include "jsxml.h" #endif #if JS_HAS_XDR #include "jsxdrapi.h" #endif #ifdef JS_THREADSAFE #define NATIVE_DROP_PROPERTY js_DropProperty extern void js_DropProperty(JSContext *cx, JSObject *obj, JSProperty *prop); #else #define NATIVE_DROP_PROPERTY NULL #endif JS_FRIEND_DATA(JSObjectOps) js_ObjectOps = { js_NewObjectMap, js_DestroyObjectMap, js_LookupProperty, js_DefineProperty, js_GetProperty, js_SetProperty, js_GetAttributes, js_SetAttributes, js_DeleteProperty, js_DefaultValue, js_Enumerate, js_CheckAccess, NULL, NATIVE_DROP_PROPERTY, js_Call, js_Construct, NULL, js_HasInstance, js_SetProtoOrParent, js_SetProtoOrParent, js_Mark, js_Clear, js_GetRequiredSlot, js_SetRequiredSlot }; JSClass js_ObjectClass = { js_Object_str, JSCLASS_HAS_CACHED_PROTO(JSProto_Object), JS_PropertyStub, JS_PropertyStub, JS_PropertyStub, JS_PropertyStub, JS_EnumerateStub, JS_ResolveStub, JS_ConvertStub, JS_FinalizeStub, JSCLASS_NO_OPTIONAL_MEMBERS }; #if JS_HAS_OBJ_PROTO_PROP static JSBool obj_getSlot(JSContext *cx, JSObject *obj, jsval id, jsval *vp); static JSBool obj_setSlot(JSContext *cx, JSObject *obj, jsval id, jsval *vp); static JSBool obj_getCount(JSContext *cx, JSObject *obj, jsval id, jsval *vp); static JSPropertySpec object_props[] = { /* These two must come first; see object_props[slot].name usage below. */ {js_proto_str, JSSLOT_PROTO, JSPROP_PERMANENT|JSPROP_SHARED, obj_getSlot, obj_setSlot}, {js_parent_str,JSSLOT_PARENT,JSPROP_READONLY|JSPROP_PERMANENT|JSPROP_SHARED, obj_getSlot, obj_setSlot}, {js_count_str, 0, JSPROP_PERMANENT,obj_getCount, obj_getCount}, {0,0,0,0,0} }; /* NB: JSSLOT_PROTO and JSSLOT_PARENT are already indexes into object_props. */ #define JSSLOT_COUNT 2 static JSBool ReportStrictSlot(JSContext *cx, uint32 slot) { if (slot == JSSLOT_PROTO) return JS_TRUE; return JS_ReportErrorFlagsAndNumber(cx, JSREPORT_WARNING | JSREPORT_STRICT, js_GetErrorMessage, NULL, JSMSG_DEPRECATED_USAGE, object_props[slot].name); } static JSBool obj_getSlot(JSContext *cx, JSObject *obj, jsval id, jsval *vp) { uint32 slot; jsid propid; JSAccessMode mode; uintN attrs; JSObject *pobj; JSClass *clasp; JSExtendedClass *xclasp; slot = (uint32) JSVAL_TO_INT(id); if (id == INT_TO_JSVAL(JSSLOT_PROTO)) { propid = ATOM_TO_JSID(cx->runtime->atomState.protoAtom); mode = JSACC_PROTO; } else { propid = ATOM_TO_JSID(cx->runtime->atomState.parentAtom); mode = JSACC_PARENT; } /* Let OBJ_CHECK_ACCESS get the slot's value, based on the access mode. */ if (!OBJ_CHECK_ACCESS(cx, obj, propid, mode, vp, &attrs)) return JS_FALSE; pobj = JSVAL_TO_OBJECT(*vp); if (pobj) { clasp = OBJ_GET_CLASS(cx, pobj); if (clasp == &js_CallClass || clasp == &js_BlockClass) { /* Censor activations and lexical scopes per ECMA-262. */ *vp = JSVAL_NULL; } else if (clasp->flags & JSCLASS_IS_EXTENDED) { xclasp = (JSExtendedClass *) clasp; if (xclasp->outerObject) { pobj = xclasp->outerObject(cx, pobj); if (!pobj) return JS_FALSE; *vp = OBJECT_TO_JSVAL(pobj); } } } return JS_TRUE; } static JSBool obj_setSlot(JSContext *cx, JSObject *obj, jsval id, jsval *vp) { JSObject *pobj; uint32 slot; jsid propid; uintN attrs; if (!JSVAL_IS_OBJECT(*vp)) return JS_TRUE; pobj = JSVAL_TO_OBJECT(*vp); if (pobj) { /* * Innerize pobj here to avoid sticking unwanted properties on the * outer object. This ensures that any with statements only grant * access to the inner object. */ OBJ_TO_INNER_OBJECT(cx, pobj); if (!pobj) return JS_FALSE; } slot = (uint32) JSVAL_TO_INT(id); if (JS_HAS_STRICT_OPTION(cx) && !ReportStrictSlot(cx, slot)) return JS_FALSE; /* __parent__ is readonly and permanent, only __proto__ may be set. */ propid = ATOM_TO_JSID(cx->runtime->atomState.protoAtom); if (!OBJ_CHECK_ACCESS(cx, obj, propid, JSACC_PROTO|JSACC_WRITE, vp, &attrs)) return JS_FALSE; return js_SetProtoOrParent(cx, obj, slot, pobj); } static JSBool obj_getCount(JSContext *cx, JSObject *obj, jsval id, jsval *vp) { jsval iter_state; jsid num_properties; JSBool ok; if (JS_HAS_STRICT_OPTION(cx) && !ReportStrictSlot(cx, JSSLOT_COUNT)) return JS_FALSE; /* Get the number of properties to enumerate. */ iter_state = JSVAL_NULL; ok = OBJ_ENUMERATE(cx, obj, JSENUMERATE_INIT, &iter_state, &num_properties); if (!ok) goto out; if (!JSVAL_IS_INT(num_properties)) { JS_ASSERT(0); *vp = JSVAL_ZERO; goto out; } *vp = num_properties; out: if (iter_state != JSVAL_NULL) ok = OBJ_ENUMERATE(cx, obj, JSENUMERATE_DESTROY, &iter_state, 0); return ok; } #else /* !JS_HAS_OBJ_PROTO_PROP */ #define object_props NULL #endif /* !JS_HAS_OBJ_PROTO_PROP */ JSBool js_SetProtoOrParent(JSContext *cx, JSObject *obj, uint32 slot, JSObject *pobj) { JSRuntime *rt; JSObject *obj2, *oldproto; JSScope *scope, *newscope; /* * Serialize all proto and parent setting in order to detect cycles. * We nest locks in this function, and only here, in the following orders: * * (1) rt->setSlotLock < pobj's scope lock; * rt->setSlotLock < pobj's proto-or-parent's scope lock; * rt->setSlotLock < pobj's grand-proto-or-parent's scope lock; * etc... * (2) rt->setSlotLock < obj's scope lock < pobj's scope lock. * * We avoid AB-BA deadlock by restricting obj from being on pobj's parent * or proto chain (pobj may already be on obj's parent or proto chain; it * could be moving up or down). We finally order obj with respect to pobj * at the bottom of this routine (just before releasing rt->setSlotLock), * by making pobj be obj's prototype or parent. * * After we have set the slot and released rt->setSlotLock, another call * to js_SetProtoOrParent could nest locks according to the first order * list above, but it cannot deadlock with any other thread. For there * to be a deadlock, other parts of the engine would have to nest scope * locks in the opposite order. XXXbe ensure they don't! */ rt = cx->runtime; #ifdef JS_THREADSAFE JS_ACQUIRE_LOCK(rt->setSlotLock); while (rt->setSlotBusy) { jsrefcount saveDepth; /* Take pains to avoid nesting rt->gcLock inside rt->setSlotLock! */ JS_RELEASE_LOCK(rt->setSlotLock); saveDepth = JS_SuspendRequest(cx); JS_ACQUIRE_LOCK(rt->setSlotLock); if (rt->setSlotBusy) JS_WAIT_CONDVAR(rt->setSlotDone, JS_NO_TIMEOUT); JS_RELEASE_LOCK(rt->setSlotLock); JS_ResumeRequest(cx, saveDepth); JS_ACQUIRE_LOCK(rt->setSlotLock); } rt->setSlotBusy = JS_TRUE; JS_RELEASE_LOCK(rt->setSlotLock); #define SET_SLOT_DONE(rt) \ JS_BEGIN_MACRO \ JS_ACQUIRE_LOCK((rt)->setSlotLock); \ (rt)->setSlotBusy = JS_FALSE; \ JS_NOTIFY_ALL_CONDVAR((rt)->setSlotDone); \ JS_RELEASE_LOCK((rt)->setSlotLock); \ JS_END_MACRO #else #define SET_SLOT_DONE(rt) /* nothing */ #endif obj2 = pobj; while (obj2) { if (obj2 == obj) { SET_SLOT_DONE(rt); JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_CYCLIC_VALUE, #if JS_HAS_OBJ_PROTO_PROP object_props[slot].name #else (slot == JSSLOT_PROTO) ? js_proto_str : js_parent_str #endif ); return JS_FALSE; } obj2 = JSVAL_TO_OBJECT(OBJ_GET_SLOT(cx, obj2, slot)); } if (slot == JSSLOT_PROTO && OBJ_IS_NATIVE(obj)) { /* Check to see whether obj shares its prototype's scope. */ JS_LOCK_OBJ(cx, obj); scope = OBJ_SCOPE(obj); oldproto = JSVAL_TO_OBJECT(LOCKED_OBJ_GET_SLOT(obj, JSSLOT_PROTO)); if (oldproto && OBJ_SCOPE(oldproto) == scope) { /* Either obj needs a new empty scope, or it should share pobj's. */ if (!pobj || !OBJ_IS_NATIVE(pobj) || OBJ_GET_CLASS(cx, pobj) != LOCKED_OBJ_GET_CLASS(oldproto)) { /* * With no proto and no scope of its own, obj is truly empty. * * If pobj is not native, obj needs its own empty scope -- it * should not continue to share oldproto's scope once oldproto * is not on obj's prototype chain. That would put properties * from oldproto's scope ahead of properties defined by pobj, * in lookup order. * * If pobj's class differs from oldproto's, we may need a new * scope to handle differences in private and reserved slots, * so we suboptimally but safely make one. */ scope = js_GetMutableScope(cx, obj); if (!scope) { JS_UNLOCK_OBJ(cx, obj); SET_SLOT_DONE(rt); return JS_FALSE; } } else if (OBJ_SCOPE(pobj) != scope) { #ifdef JS_THREADSAFE /* * We are about to nest scope locks. Help jslock.c:ShareScope * keep scope->u.count balanced for the JS_UNLOCK_SCOPE, while * avoiding deadlock, by recording scope in rt->setSlotScope. */ if (scope->ownercx) { JS_ASSERT(scope->ownercx == cx); rt->setSlotScope = scope; } #endif /* We can't deadlock because we checked for cycles above (2). */ JS_LOCK_OBJ(cx, pobj); newscope = (JSScope *) js_HoldObjectMap(cx, pobj->map); obj->map = &newscope->map; js_DropObjectMap(cx, &scope->map, obj); JS_TRANSFER_SCOPE_LOCK(cx, scope, newscope); scope = newscope; #ifdef JS_THREADSAFE rt->setSlotScope = NULL; #endif } } LOCKED_OBJ_SET_SLOT(obj, JSSLOT_PROTO, OBJECT_TO_JSVAL(pobj)); JS_UNLOCK_SCOPE(cx, scope); } else { OBJ_SET_SLOT(cx, obj, slot, OBJECT_TO_JSVAL(pobj)); } SET_SLOT_DONE(rt); return JS_TRUE; #undef SET_SLOT_DONE } JS_STATIC_DLL_CALLBACK(JSHashNumber) js_hash_object(const void *key) { return (JSHashNumber)JS_PTR_TO_UINT32(key) >> JSVAL_TAGBITS; } static JSHashEntry * MarkSharpObjects(JSContext *cx, JSObject *obj, JSIdArray **idap) { JSSharpObjectMap *map; JSHashTable *table; JSHashNumber hash; JSHashEntry **hep, *he; jsatomid sharpid; JSIdArray *ida; JSBool ok; jsint i, length; jsid id; #if JS_HAS_GETTER_SETTER JSObject *obj2; JSProperty *prop; uintN attrs; #endif jsval val; int stackDummy; if (!JS_CHECK_STACK_SIZE(cx, stackDummy)) { JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_OVER_RECURSED); return NULL; } map = &cx->sharpObjectMap; table = map->table; hash = js_hash_object(obj); hep = JS_HashTableRawLookup(table, hash, obj); he = *hep; if (!he) { sharpid = 0; he = JS_HashTableRawAdd(table, hep, hash, obj, JS_UINT32_TO_PTR(sharpid)); if (!he) { JS_ReportOutOfMemory(cx); return NULL; } /* * Increment map->depth to protect js_EnterSharpObject from reentering * itself badly. Without this fix, if we reenter the basis case where * map->depth == 0, when unwinding the inner call we will destroy the * newly-created hash table and crash. */ ++map->depth; ida = JS_Enumerate(cx, obj); --map->depth; if (!ida) return NULL; ok = JS_TRUE; for (i = 0, length = ida->length; i < length; i++) { id = ida->vector[i]; #if JS_HAS_GETTER_SETTER ok = OBJ_LOOKUP_PROPERTY(cx, obj, id, &obj2, &prop); if (!ok) break; if (!prop) continue; ok = OBJ_GET_ATTRIBUTES(cx, obj2, id, prop, &attrs); if (ok) { if (OBJ_IS_NATIVE(obj2) && (attrs & (JSPROP_GETTER | JSPROP_SETTER))) { val = JSVAL_NULL; if (attrs & JSPROP_GETTER) val = (jsval) ((JSScopeProperty*)prop)->getter; if (attrs & JSPROP_SETTER) { if (val != JSVAL_NULL) { /* Mark the getter, then set val to setter. */ ok = (MarkSharpObjects(cx, JSVAL_TO_OBJECT(val), NULL) != NULL); } val = (jsval) ((JSScopeProperty*)prop)->setter; } } else { ok = OBJ_GET_PROPERTY(cx, obj, id, &val); } } OBJ_DROP_PROPERTY(cx, obj2, prop); #else ok = OBJ_GET_PROPERTY(cx, obj, id, &val); #endif if (!ok) break; if (!JSVAL_IS_PRIMITIVE(val) && !MarkSharpObjects(cx, JSVAL_TO_OBJECT(val), NULL)) { ok = JS_FALSE; break; } } if (!ok || !idap) JS_DestroyIdArray(cx, ida); if (!ok) return NULL; } else { sharpid = JS_PTR_TO_UINT32(he->value); if (sharpid == 0) { sharpid = ++map->sharpgen << SHARP_ID_SHIFT; he->value = JS_UINT32_TO_PTR(sharpid); } ida = NULL; } if (idap) *idap = ida; return he; } JSHashEntry * js_EnterSharpObject(JSContext *cx, JSObject *obj, JSIdArray **idap, jschar **sp) { JSSharpObjectMap *map; JSHashTable *table; JSIdArray *ida; JSHashNumber hash; JSHashEntry *he, **hep; jsatomid sharpid; char buf[20]; size_t len; if (JS_HAS_NATIVE_BRANCH_CALLBACK_OPTION(cx) && cx->branchCallback && !cx->branchCallback(cx, NULL)) { return NULL; } /* Set to null in case we return an early error. */ *sp = NULL; map = &cx->sharpObjectMap; table = map->table; if (!table) { table = JS_NewHashTable(8, js_hash_object, JS_CompareValues, JS_CompareValues, NULL, NULL); if (!table) { JS_ReportOutOfMemory(cx); return NULL; } map->table = table; JS_KEEP_ATOMS(cx->runtime); } /* From this point the control must flow either through out: or bad:. */ ida = NULL; if (map->depth == 0) { he = MarkSharpObjects(cx, obj, &ida); if (!he) goto bad; JS_ASSERT((JS_PTR_TO_UINT32(he->value) & SHARP_BIT) == 0); if (!idap) { JS_DestroyIdArray(cx, ida); ida = NULL; } } else { hash = js_hash_object(obj); hep = JS_HashTableRawLookup(table, hash, obj); he = *hep; /* * It's possible that the value of a property has changed from the * first time the object's properties are traversed (when the property * ids are entered into the hash table) to the second (when they are * converted to strings), i.e., the OBJ_GET_PROPERTY() call is not * idempotent. */ if (!he) { he = JS_HashTableRawAdd(table, hep, hash, obj, NULL); if (!he) { JS_ReportOutOfMemory(cx); goto bad; } sharpid = 0; goto out; } } sharpid = JS_PTR_TO_UINT32(he->value); if (sharpid != 0) { len = JS_snprintf(buf, sizeof buf, "#%u%c", sharpid >> SHARP_ID_SHIFT, (sharpid & SHARP_BIT) ? '#' : '='); *sp = js_InflateString(cx, buf, &len); if (!*sp) { if (ida) JS_DestroyIdArray(cx, ida); goto bad; } } out: JS_ASSERT(he); if ((sharpid & SHARP_BIT) == 0) { if (idap && !ida) { ida = JS_Enumerate(cx, obj); if (!ida) { if (*sp) { JS_free(cx, *sp); *sp = NULL; } goto bad; } } map->depth++; } if (idap) *idap = ida; return he; bad: /* Clean up the sharpObjectMap table on outermost error. */ if (map->depth == 0) { JS_UNKEEP_ATOMS(cx->runtime); map->sharpgen = 0; JS_HashTableDestroy(map->table); map->table = NULL; } return NULL; } void js_LeaveSharpObject(JSContext *cx, JSIdArray **idap) { JSSharpObjectMap *map; JSIdArray *ida; map = &cx->sharpObjectMap; JS_ASSERT(map->depth > 0); if (--map->depth == 0) { JS_UNKEEP_ATOMS(cx->runtime); map->sharpgen = 0; JS_HashTableDestroy(map->table); map->table = NULL; } if (idap) { ida = *idap; if (ida) { JS_DestroyIdArray(cx, ida); *idap = NULL; } } } JS_STATIC_DLL_CALLBACK(intN) gc_sharp_table_entry_marker(JSHashEntry *he, intN i, void *arg) { GC_MARK((JSContext *)arg, (JSObject *)he->key, "sharp table entry"); return JS_DHASH_NEXT; } void js_GCMarkSharpMap(JSContext *cx, JSSharpObjectMap *map) { JS_ASSERT(map->depth > 0); JS_ASSERT(map->table); /* * During recursive calls to MarkSharpObjects a non-native object or * object with a custom getProperty method can potentially return an * unrooted value or even cut from the object graph an argument of one of * MarkSharpObjects recursive invocations. So we must protect map->table * entries against GC. * * We can not simply use JSTempValueRooter to mark the obj argument of * MarkSharpObjects during recursion as we have to protect *all* entries * in JSSharpObjectMap including those that contains otherwise unreachable * objects just allocated through custom getProperty. Otherwise newer * allocations can re-use the address of an object stored in the hashtable * confusing js_EnterSharpObject. So to address the problem we simply * mark all objects from map->table. * * An alternative "proper" solution is to use JSTempValueRooter in * MarkSharpObjects with code to remove during finalization entries * with otherwise unreachable objects. But this is way too complex * to justify spending efforts. */ JS_HashTableEnumerateEntries(map->table, gc_sharp_table_entry_marker, cx); } #define OBJ_TOSTRING_EXTRA 4 /* for 4 local GC roots */ #if JS_HAS_TOSOURCE JSBool js_obj_toSource(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) { JSBool ok, outermost; JSHashEntry *he; JSIdArray *ida; jschar *chars, *ochars, *vsharp; const jschar *idstrchars, *vchars; size_t nchars, idstrlength, gsoplength, vlength, vsharplength, curlen; char *comma; jsint i, j, length, valcnt; jsid id; #if JS_HAS_GETTER_SETTER JSObject *obj2; JSProperty *prop; uintN attrs; #endif jsval *val; JSString *gsopold[2]; JSString *gsop[2]; JSAtom *atom; JSString *idstr, *valstr, *str; int stackDummy; if (!JS_CHECK_STACK_SIZE(cx, stackDummy)) { JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_OVER_RECURSED); return JS_FALSE; } /* If outermost, we need parentheses to be an expression, not a block. */ outermost = (cx->sharpObjectMap.depth == 0); he = js_EnterSharpObject(cx, obj, &ida, &chars); if (!he) return JS_FALSE; if (IS_SHARP(he)) { /* * We didn't enter -- obj is already "sharp", meaning we've visited it * already in our depth first search, and therefore chars contains a * string of the form "#n#". */ JS_ASSERT(!ida); #if JS_HAS_SHARP_VARS nchars = js_strlen(chars); #else chars[0] = '{'; chars[1] = '}'; chars[2] = 0; nchars = 2; #endif goto make_string; } JS_ASSERT(ida); ok = JS_TRUE; if (!chars) { /* If outermost, allocate 4 + 1 for "({})" and the terminator. */ chars = (jschar *) malloc(((outermost ? 4 : 2) + 1) * sizeof(jschar)); nchars = 0; if (!chars) goto error; if (outermost) chars[nchars++] = '('; } else { /* js_EnterSharpObject returned a string of the form "#n=" in chars. */ MAKE_SHARP(he); nchars = js_strlen(chars); chars = (jschar *) realloc((ochars = chars), (nchars + 2 + 1) * sizeof(jschar)); if (!chars) { free(ochars); goto error; } if (outermost) { /* * No need for parentheses around the whole shebang, because #n= * unambiguously begins an object initializer, and never a block * statement. */ outermost = JS_FALSE; } } #ifdef DUMP_CALL_TABLE if (cx->options & JSOPTION_LOGCALL_TOSOURCE) { const char *classname = OBJ_GET_CLASS(cx, obj)->name; size_t classnchars = strlen(classname); static const char classpropid[] = "C"; const char *cp; size_t onchars = nchars; /* 2 for ': ', 2 quotes around classname, 2 for ', ' after. */ classnchars += sizeof classpropid - 1 + 2 + 2; if (ida->length) classnchars += 2; /* 2 for the braces, 1 for the terminator */ chars = (jschar *) realloc((ochars = chars), (nchars + classnchars + 2 + 1) * sizeof(jschar)); if (!chars) { free(ochars); goto error; } chars[nchars++] = '{'; /* 1 from the 2 braces */ for (cp = classpropid; *cp; cp++) chars[nchars++] = (jschar) *cp; chars[nchars++] = ':'; chars[nchars++] = ' '; /* 2 for ': ' */ chars[nchars++] = '"'; for (cp = classname; *cp; cp++) chars[nchars++] = (jschar) *cp; chars[nchars++] = '"'; /* 2 quotes */ if (ida->length) { chars[nchars++] = ','; chars[nchars++] = ' '; /* 2 for ', ' */ } JS_ASSERT(nchars - onchars == 1 + classnchars); } else #endif chars[nchars++] = '{'; comma = NULL; /* * We have four local roots for cooked and raw value GC safety. Hoist the * "argv + 2" out of the loop using the val local, which refers to the raw * (unconverted, "uncooked") values. */ val = argv + 2; for (i = 0, length = ida->length; i < length; i++) { JSBool idIsLexicalIdentifier, needOldStyleGetterSetter; /* Get strings for id and value and GC-root them via argv. */ id = ida->vector[i]; #if JS_HAS_GETTER_SETTER ok = OBJ_LOOKUP_PROPERTY(cx, obj, id, &obj2, &prop); if (!ok) goto error; #endif /* * Convert id to a jsval and then to a string. Decide early whether we * prefer get/set or old getter/setter syntax. */ atom = JSID_IS_ATOM(id) ? JSID_TO_ATOM(id) : NULL; idstr = js_ValueToString(cx, ID_TO_VALUE(id)); if (!idstr) { ok = JS_FALSE; OBJ_DROP_PROPERTY(cx, obj2, prop); goto error; } *rval = STRING_TO_JSVAL(idstr); /* local root */ idIsLexicalIdentifier = js_IsIdentifier(idstr); needOldStyleGetterSetter = !idIsLexicalIdentifier || js_CheckKeyword(JSSTRING_CHARS(idstr), JSSTRING_LENGTH(idstr)) != TOK_EOF; #if JS_HAS_GETTER_SETTER valcnt = 0; if (prop) { ok = OBJ_GET_ATTRIBUTES(cx, obj2, id, prop, &attrs); if (!ok) { OBJ_DROP_PROPERTY(cx, obj2, prop); goto error; } if (OBJ_IS_NATIVE(obj2) && (attrs & (JSPROP_GETTER | JSPROP_SETTER))) { if (attrs & JSPROP_GETTER) { val[valcnt] = (jsval) ((JSScopeProperty *)prop)->getter; gsopold[valcnt] = ATOM_TO_STRING(cx->runtime->atomState.getterAtom); gsop[valcnt] = ATOM_TO_STRING(cx->runtime->atomState.getAtom); valcnt++; } if (attrs & JSPROP_SETTER) { val[valcnt] = (jsval) ((JSScopeProperty *)prop)->setter; gsopold[valcnt] = ATOM_TO_STRING(cx->runtime->atomState.setterAtom); gsop[valcnt] = ATOM_TO_STRING(cx->runtime->atomState.setAtom); valcnt++; } } else { valcnt = 1; gsop[0] = NULL; gsopold[0] = NULL; ok = OBJ_GET_PROPERTY(cx, obj, id, &val[0]); } OBJ_DROP_PROPERTY(cx, obj2, prop); } #else /* !JS_HAS_GETTER_SETTER */ /* * We simplify the source code at the price of minor dead code bloat in * the ECMA version (for testing only, see jsconfig.h). The null * default values in gsop[j] suffice to disable non-ECMA getter and * setter code. */ valcnt = 1; gsop[0] = NULL; gsopold[0] = NULL; ok = OBJ_GET_PROPERTY(cx, obj, id, &val[0]); #endif /* !JS_HAS_GETTER_SETTER */ if (!ok) goto error; /* * If id is a string that's not an identifier, then it needs to be * quoted. Also, negative integer ids must be quoted. */ if (atom ? !idIsLexicalIdentifier : (JSID_IS_OBJECT(id) || JSID_TO_INT(id) < 0)) { idstr = js_QuoteString(cx, idstr, (jschar)'\''); if (!idstr) { ok = JS_FALSE; goto error; } *rval = STRING_TO_JSVAL(idstr); /* local root */ } idstrchars = JSSTRING_CHARS(idstr); idstrlength = JSSTRING_LENGTH(idstr); for (j = 0; j < valcnt; j++) { /* Convert val[j] to its canonical source form. */ valstr = js_ValueToSource(cx, val[j]); if (!valstr) { ok = JS_FALSE; goto error; } argv[j] = STRING_TO_JSVAL(valstr); /* local root */ vchars = JSSTRING_CHARS(valstr); vlength = JSSTRING_LENGTH(valstr); if (vchars[0] == '#') needOldStyleGetterSetter = JS_TRUE; if (needOldStyleGetterSetter) gsop[j] = gsopold[j]; #ifndef OLD_GETTER_SETTER /* * Remove '(function ' from the beginning of valstr and ')' from the * end so that we can put "get" in front of the function definition. */ if (gsop[j] && VALUE_IS_FUNCTION(cx, val[j]) && !needOldStyleGetterSetter) { const jschar *start = vchars; if (vchars[0] == '(') vchars++; vchars = js_strchr_limit(vchars, '(', vchars + vlength); if (vchars) { vlength -= vchars - start + 1; } else { gsop[j] = NULL; vchars = start; } } #else needOldStyleGetterSetter = JS_TRUE; gsop[j] = gsopold[j]; #endif /* If val[j] is a non-sharp object, consider sharpening it. */ vsharp = NULL; vsharplength = 0; #if JS_HAS_SHARP_VARS if (!JSVAL_IS_PRIMITIVE(val[j]) && vchars[0] != '#') { he = js_EnterSharpObject(cx, JSVAL_TO_OBJECT(val[j]), NULL, &vsharp); if (!he) { ok = JS_FALSE; goto error; } if (IS_SHARP(he)) { vchars = vsharp; vlength = js_strlen(vchars); needOldStyleGetterSetter = JS_TRUE; gsop[j] = gsopold[j]; } else { if (vsharp) { vsharplength = js_strlen(vsharp); MAKE_SHARP(he); needOldStyleGetterSetter = JS_TRUE; gsop[j] = gsopold[j]; } js_LeaveSharpObject(cx, NULL); } } #endif #define SAFE_ADD(n) \ JS_BEGIN_MACRO \ size_t n_ = (n); \ curlen += n_; \ if (curlen < n_) \ goto overflow; \ JS_END_MACRO curlen = nchars; if (comma) SAFE_ADD(2); SAFE_ADD(idstrlength + 1); if (gsop[j]) SAFE_ADD(JSSTRING_LENGTH(gsop[j]) + 1); SAFE_ADD(vsharplength); SAFE_ADD(vlength); /* Account for the trailing null. */ SAFE_ADD((outermost ? 2 : 1) + 1); #undef SAFE_ADD if (curlen > (size_t)-1 / sizeof(jschar)) goto overflow; /* Allocate 1 + 1 at end for closing brace and terminating 0. */ chars = (jschar *) realloc((ochars = chars), curlen * sizeof(jschar)); if (!chars) { /* Save code space on error: let JS_free ignore null vsharp. */ JS_free(cx, vsharp); free(ochars); goto error; } if (comma) { chars[nchars++] = comma[0]; chars[nchars++] = comma[1]; } comma = ", "; if (needOldStyleGetterSetter) { js_strncpy(&chars[nchars], idstrchars, idstrlength); nchars += idstrlength; if (gsop[j]) { chars[nchars++] = ' '; gsoplength = JSSTRING_LENGTH(gsop[j]); js_strncpy(&chars[nchars], JSSTRING_CHARS(gsop[j]), gsoplength); nchars += gsoplength; } chars[nchars++] = ':'; } else { /* New style "decompilation" */ if (gsop[j]) { gsoplength = JSSTRING_LENGTH(gsop[j]); js_strncpy(&chars[nchars], JSSTRING_CHARS(gsop[j]), gsoplength); nchars += gsoplength; chars[nchars++] = ' '; } js_strncpy(&chars[nchars], idstrchars, idstrlength); nchars += idstrlength; /* Extraneous space after id here will be extracted later */ chars[nchars++] = gsop[j] ? ' ' : ':'; } if (vsharplength) { js_strncpy(&chars[nchars], vsharp, vsharplength); nchars += vsharplength; } js_strncpy(&chars[nchars], vchars, vlength); nchars += vlength; if (vsharp) JS_free(cx, vsharp); #ifdef DUMP_CALL_TABLE if (outermost && nchars >= js_LogCallToSourceLimit) break; #endif } } chars[nchars++] = '}'; if (outermost) chars[nchars++] = ')'; chars[nchars] = 0; error: js_LeaveSharpObject(cx, &ida); if (!ok) { if (chars) free(chars); return ok; } if (!chars) { JS_ReportOutOfMemory(cx); return JS_FALSE; } make_string: str = js_NewString(cx, chars, nchars, 0); if (!str) { free(chars); return JS_FALSE; } *rval = STRING_TO_JSVAL(str); return JS_TRUE; overflow: JS_free(cx, vsharp); free(chars); chars = NULL; goto error; } #endif /* JS_HAS_TOSOURCE */ JSBool js_obj_toString(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) { jschar *chars; size_t nchars; const char *clazz, *prefix; JSString *str; clazz = OBJ_GET_CLASS(cx, obj)->name; nchars = 9 + strlen(clazz); /* 9 for "[object ]" */ chars = (jschar *) JS_malloc(cx, (nchars + 1) * sizeof(jschar)); if (!chars) return JS_FALSE; prefix = "[object "; nchars = 0; while ((chars[nchars] = (jschar)*prefix) != 0) nchars++, prefix++; while ((chars[nchars] = (jschar)*clazz) != 0) nchars++, clazz++; chars[nchars++] = ']'; chars[nchars] = 0; str = js_NewString(cx, chars, nchars, 0); if (!str) { JS_free(cx, chars); return JS_FALSE; } *rval = STRING_TO_JSVAL(str); return JS_TRUE; } static JSBool js_obj_toLocaleString(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) { JSString *str; str = js_ValueToString(cx, argv[-1]); if (!str) return JS_FALSE; *rval = STRING_TO_JSVAL(str); return JS_TRUE; } static JSBool obj_valueOf(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) { *rval = OBJECT_TO_JSVAL(obj); return JS_TRUE; } /* * Check whether principals subsumes scopeobj's principals, and return true * if so (or if scopeobj has no principals, for backward compatibility with * the JS API, which does not require principals), and false otherwise. */ JSBool js_CheckPrincipalsAccess(JSContext *cx, JSObject *scopeobj, JSPrincipals *principals, JSAtom *caller) { JSRuntime *rt; JSPrincipals *scopePrincipals; const char *callerstr; rt = cx->runtime; if (rt->findObjectPrincipals) { scopePrincipals = rt->findObjectPrincipals(cx, scopeobj); if (!principals || !scopePrincipals || !principals->subsume(principals, scopePrincipals)) { callerstr = js_AtomToPrintableString(cx, caller); if (!callerstr) return JS_FALSE; JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_BAD_INDIRECT_CALL, callerstr); return JS_FALSE; } } return JS_TRUE; } JSObject * js_CheckScopeChainValidity(JSContext *cx, JSObject *scopeobj, const char *caller) { JSClass *clasp; JSExtendedClass *xclasp; JSObject *inner; if (!scopeobj) goto bad; OBJ_TO_INNER_OBJECT(cx, scopeobj); if (!scopeobj) return NULL; inner = scopeobj; /* XXX This is an awful gross hack. */ while (scopeobj) { clasp = OBJ_GET_CLASS(cx, scopeobj); if (clasp->flags & JSCLASS_IS_EXTENDED) { xclasp = (JSExtendedClass*)clasp; if (xclasp->innerObject && xclasp->innerObject(cx, scopeobj) != scopeobj) { goto bad; } } scopeobj = OBJ_GET_PARENT(cx, scopeobj); } return inner; bad: JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_BAD_INDIRECT_CALL, caller); return NULL; } static JSBool obj_eval(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) { JSStackFrame *fp, *caller; JSBool indirectCall; JSObject *scopeobj; JSString *str; const char *file; uintN line; JSPrincipals *principals; JSScript *script; JSBool ok; #if JS_HAS_EVAL_THIS_SCOPE JSObject *callerScopeChain = NULL, *callerVarObj = NULL; JSObject *setCallerScopeChain = NULL; JSBool setCallerVarObj = JS_FALSE; #endif fp = cx->fp; caller = JS_GetScriptedCaller(cx, fp); JS_ASSERT(!caller || caller->pc); indirectCall = (caller && *caller->pc != JSOP_EVAL); if (indirectCall && !JS_ReportErrorFlagsAndNumber(cx, JSREPORT_WARNING | JSREPORT_STRICT, js_GetErrorMessage, NULL, JSMSG_BAD_INDIRECT_CALL, js_eval_str)) { return JS_FALSE; } if (!JSVAL_IS_STRING(argv[0])) { *rval = argv[0]; return JS_TRUE; } /* * If the caller is a lightweight function and doesn't have a variables * object, then we need to provide one for the compiler to stick any * declared (var) variables into. */ if (caller && !caller->varobj && !js_GetCallObject(cx, caller, NULL)) return JS_FALSE; #if JS_HAS_SCRIPT_OBJECT /* * Script.prototype.compile/exec and Object.prototype.eval all take an * optional trailing argument that overrides the scope object. */ scopeobj = NULL; if (argc >= 2) { if (!js_ValueToObject(cx, argv[1], &scopeobj)) return JS_FALSE; argv[1] = OBJECT_TO_JSVAL(scopeobj); } if (!scopeobj) #endif { #if JS_HAS_EVAL_THIS_SCOPE /* If obj.eval(str), emulate 'with (obj) eval(str)' in the caller. */ if (indirectCall) { callerScopeChain = js_GetScopeChain(cx, caller); if (!callerScopeChain) return JS_FALSE; OBJ_TO_INNER_OBJECT(cx, obj); if (!obj) return JS_FALSE; if (obj != callerScopeChain) { if (!js_CheckPrincipalsAccess(cx, obj, caller->script->principals, cx->runtime->atomState.evalAtom)) { return JS_FALSE; } scopeobj = js_NewWithObject(cx, obj, callerScopeChain, -1); if (!scopeobj) return JS_FALSE; /* Set fp->scopeChain too, for the compiler. */ caller->scopeChain = fp->scopeChain = scopeobj; /* Remember scopeobj so we can null its private when done. */ setCallerScopeChain = scopeobj; } callerVarObj = caller->varobj; if (obj != callerVarObj) { /* Set fp->varobj too, for the compiler. */ caller->varobj = fp->varobj = obj; setCallerVarObj = JS_TRUE; } } /* From here on, control must exit through label out with ok set. */ #endif /* Compile using caller's current scope object. */ if (caller) { scopeobj = js_GetScopeChain(cx, caller); if (!scopeobj) { ok = JS_FALSE; goto out; } } } /* Ensure we compile this eval with the right object in the scope chain. */ scopeobj = js_CheckScopeChainValidity(cx, scopeobj, js_eval_str); if (!scopeobj) return JS_FALSE; str = JSVAL_TO_STRING(argv[0]); if (caller) { principals = JS_EvalFramePrincipals(cx, fp, caller); if (principals == caller->script->principals) { file = caller->script->filename; line = js_PCToLineNumber(cx, caller->script, caller->pc); } else { file = principals->codebase; line = 0; } } else { file = NULL; line = 0; principals = NULL; } /* * Set JSFRAME_EVAL on fp and any frames (e.g., fun_call if eval.call was * invoked) between fp and its scripted caller, to help the compiler easily * find the same caller whose scope and var obj we've set. * * XXX this nonsense could, and perhaps should, go away with a better way * to pass params to the compiler than via the top-most frame. */ do { fp->flags |= JSFRAME_EVAL; } while ((fp = fp->down) != caller); script = JS_CompileUCScriptForPrincipals(cx, scopeobj, principals, JSSTRING_CHARS(str), JSSTRING_LENGTH(str), file, line); if (!script) { ok = JS_FALSE; goto out; } #if JS_HAS_SCRIPT_OBJECT if (argc < 2) #endif { /* Execute using caller's new scope object (might be a Call object). */ if (caller) scopeobj = caller->scopeChain; } /* * Belt-and-braces: check that the lesser of eval's principals and the * caller's principals has access to scopeobj. */ ok = js_CheckPrincipalsAccess(cx, scopeobj, principals, cx->runtime->atomState.evalAtom); if (ok) ok = js_Execute(cx, scopeobj, script, caller, JSFRAME_EVAL, rval); JS_DestroyScript(cx, script); out: #if JS_HAS_EVAL_THIS_SCOPE /* Restore OBJ_GET_PARENT(scopeobj) not callerScopeChain in case of Call. */ if (setCallerScopeChain) { caller->scopeChain = callerScopeChain; JS_ASSERT(OBJ_GET_CLASS(cx, setCallerScopeChain) == &js_WithClass); JS_SetPrivate(cx, setCallerScopeChain, NULL); } if (setCallerVarObj) caller->varobj = callerVarObj; #endif return ok; } #if JS_HAS_OBJ_WATCHPOINT static JSBool obj_watch_handler(JSContext *cx, JSObject *obj, jsval id, jsval old, jsval *nvp, void *closure) { JSObject *callable; JSRuntime *rt; JSStackFrame *caller; JSPrincipals *subject, *watcher; JSResolvingKey key; JSResolvingEntry *entry; uint32 generation; jsval argv[3]; JSBool ok; callable = (JSObject *) closure; rt = cx->runtime; if (rt->findObjectPrincipals) { /* Skip over any obj_watch_* frames between us and the real subject. */ caller = JS_GetScriptedCaller(cx, cx->fp); if (caller) { /* * Only call the watch handler if the watcher is allowed to watch * the currently executing script. */ watcher = rt->findObjectPrincipals(cx, callable); subject = JS_StackFramePrincipals(cx, caller); if (watcher && subject && !watcher->subsume(watcher, subject)) { /* Silently don't call the watch handler. */ return JS_TRUE; } } } /* Avoid recursion on (obj, id) already being watched on cx. */ key.obj = obj; key.id = id; if (!js_StartResolving(cx, &key, JSRESFLAG_WATCH, &entry)) return JS_FALSE; if (!entry) return JS_TRUE; generation = cx->resolvingTable->generation; argv[0] = id; argv[1] = old; argv[2] = *nvp; ok = js_InternalCall(cx, obj, OBJECT_TO_JSVAL(callable), 3, argv, nvp); js_StopResolving(cx, &key, JSRESFLAG_WATCH, entry, generation); return ok; } static JSBool obj_watch(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) { JSObject *callable; jsval userid, value; jsid propid; uintN attrs; callable = js_ValueToCallableObject(cx, &argv[1], 0); if (!callable) return JS_FALSE; /* Compute the unique int/atom symbol id needed by js_LookupProperty. */ userid = argv[0]; if (!JS_ValueToId(cx, userid, &propid)) return JS_FALSE; if (!OBJ_CHECK_ACCESS(cx, obj, propid, JSACC_WATCH, &value, &attrs)) return JS_FALSE; if (attrs & JSPROP_READONLY) return JS_TRUE; return JS_SetWatchPoint(cx, obj, userid, obj_watch_handler, callable); } static JSBool obj_unwatch(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) { return JS_ClearWatchPoint(cx, obj, argv[0], NULL, NULL); } #endif /* JS_HAS_OBJ_WATCHPOINT */ /* * Prototype and property query methods, to complement the 'in' and * 'instanceof' operators. */ /* Proposed ECMA 15.2.4.5. */ static JSBool obj_hasOwnProperty(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) { return js_HasOwnPropertyHelper(cx, obj, obj->map->ops->lookupProperty, argc, argv, rval); } JSBool js_HasOwnPropertyHelper(JSContext *cx, JSObject *obj, JSLookupPropOp lookup, uintN argc, jsval *argv, jsval *rval) { jsid id; JSObject *obj2; JSProperty *prop; JSScopeProperty *sprop; if (!JS_ValueToId(cx, argv[0], &id)) return JS_FALSE; if (!lookup(cx, obj, id, &obj2, &prop)) return JS_FALSE; if (!prop) { *rval = JSVAL_FALSE; } else if (obj2 == obj) { *rval = JSVAL_TRUE; } else { JSClass *clasp; JSExtendedClass *xclasp; clasp = OBJ_GET_CLASS(cx, obj); xclasp = (clasp->flags & JSCLASS_IS_EXTENDED) ? (JSExtendedClass *)clasp : NULL; if (xclasp && xclasp->outerObject && xclasp->outerObject(cx, obj2) == obj) { *rval = JSVAL_TRUE; } else if (OBJ_IS_NATIVE(obj2) && OBJ_GET_CLASS(cx, obj2) == clasp) { /* * The combination of JSPROP_SHARED and JSPROP_PERMANENT in a * delegated property makes that property appear to be direct in * all delegating instances of the same native class. This hack * avoids bloating every function instance with its own 'length' * (AKA 'arity') property. But it must not extend across class * boundaries, to avoid making hasOwnProperty lie (bug 320854). * * It's not really a hack, of course: a permanent property can't * be deleted, and JSPROP_SHARED means "don't allocate a slot in * any instance, prototype or delegating". Without a slot, and * without the ability to remove and recreate (with differences) * the property, there is no way to tell whether it is directly * owned, or indirectly delegated. */ sprop = (JSScopeProperty *)prop; *rval = BOOLEAN_TO_JSVAL(SPROP_IS_SHARED_PERMANENT(sprop)); } else { *rval = JSVAL_FALSE; } } if (prop) OBJ_DROP_PROPERTY(cx, obj2, prop); return JS_TRUE; } /* Proposed ECMA 15.2.4.6. */ static JSBool obj_isPrototypeOf(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) { JSBool b; if (!js_IsDelegate(cx, obj, *argv, &b)) return JS_FALSE; *rval = BOOLEAN_TO_JSVAL(b); return JS_TRUE; } /* Proposed ECMA 15.2.4.7. */ static JSBool obj_propertyIsEnumerable(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) { jsid id; uintN attrs; JSObject *obj2; JSProperty *prop; JSBool ok; if (!JS_ValueToId(cx, argv[0], &id)) return JS_FALSE; if (!OBJ_LOOKUP_PROPERTY(cx, obj, id, &obj2, &prop)) return JS_FALSE; if (!prop) { *rval = JSVAL_FALSE; return JS_TRUE; } /* * XXX ECMA spec error compatible: return false unless hasOwnProperty. * The ECMA spec really should be fixed so propertyIsEnumerable and the * for..in loop agree on whether prototype properties are enumerable, * obviously by fixing this method (not by breaking the for..in loop!). * * We check here for shared permanent prototype properties, which should * be treated as if they are local to obj. They are an implementation * technique used to satisfy ECMA requirements; users should not be able * to distinguish a shared permanent proto-property from a local one. */ if (obj2 != obj && !(OBJ_IS_NATIVE(obj2) && SPROP_IS_SHARED_PERMANENT((JSScopeProperty *)prop))) { OBJ_DROP_PROPERTY(cx, obj2, prop); *rval = JSVAL_FALSE; return JS_TRUE; } ok = OBJ_GET_ATTRIBUTES(cx, obj2, id, prop, &attrs); OBJ_DROP_PROPERTY(cx, obj2, prop); if (ok) *rval = BOOLEAN_TO_JSVAL((attrs & JSPROP_ENUMERATE) != 0); return ok; } #if JS_HAS_GETTER_SETTER static JSBool obj_defineGetter(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) { jsval fval, junk; jsid id; uintN attrs; fval = argv[1]; if (JS_TypeOfValue(cx, fval) != JSTYPE_FUNCTION) { JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_BAD_GETTER_OR_SETTER, js_getter_str); return JS_FALSE; } if (!JS_ValueToId(cx, argv[0], &id)) return JS_FALSE; if (!js_CheckRedeclaration(cx, obj, id, JSPROP_GETTER, NULL, NULL)) return JS_FALSE; /* * Getters and setters are just like watchpoints from an access * control point of view. */ if (!OBJ_CHECK_ACCESS(cx, obj, id, JSACC_WATCH, &junk, &attrs)) return JS_FALSE; return OBJ_DEFINE_PROPERTY(cx, obj, id, JSVAL_VOID, (JSPropertyOp) JSVAL_TO_OBJECT(fval), NULL, JSPROP_ENUMERATE | JSPROP_GETTER | JSPROP_SHARED, NULL); } static JSBool obj_defineSetter(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) { jsval fval, junk; jsid id; uintN attrs; fval = argv[1]; if (JS_TypeOfValue(cx, fval) != JSTYPE_FUNCTION) { JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_BAD_GETTER_OR_SETTER, js_setter_str); return JS_FALSE; } if (!JS_ValueToId(cx, argv[0], &id)) return JS_FALSE; if (!js_CheckRedeclaration(cx, obj, id, JSPROP_SETTER, NULL, NULL)) return JS_FALSE; /* * Getters and setters are just like watchpoints from an access * control point of view. */ if (!OBJ_CHECK_ACCESS(cx, obj, id, JSACC_WATCH, &junk, &attrs)) return JS_FALSE; return OBJ_DEFINE_PROPERTY(cx, obj, id, JSVAL_VOID, NULL, (JSPropertyOp) JSVAL_TO_OBJECT(fval), JSPROP_ENUMERATE | JSPROP_SETTER | JSPROP_SHARED, NULL); } static JSBool obj_lookupGetter(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) { jsid id; JSObject *pobj; JSProperty *prop; JSScopeProperty *sprop; if (!JS_ValueToId(cx, argv[0], &id)) return JS_FALSE; if (!OBJ_LOOKUP_PROPERTY(cx, obj, id, &pobj, &prop)) return JS_FALSE; if (prop) { if (OBJ_IS_NATIVE(pobj)) { sprop = (JSScopeProperty *) prop; if (sprop->attrs & JSPROP_GETTER) *rval = OBJECT_TO_JSVAL(sprop->getter); } OBJ_DROP_PROPERTY(cx, pobj, prop); } return JS_TRUE; } static JSBool obj_lookupSetter(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) { jsid id; JSObject *pobj; JSProperty *prop; JSScopeProperty *sprop; if (!JS_ValueToId(cx, argv[0], &id)) return JS_FALSE; if (!OBJ_LOOKUP_PROPERTY(cx, obj, id, &pobj, &prop)) return JS_FALSE; if (prop) { if (OBJ_IS_NATIVE(pobj)) { sprop = (JSScopeProperty *) prop; if (sprop->attrs & JSPROP_SETTER) *rval = OBJECT_TO_JSVAL(sprop->setter); } OBJ_DROP_PROPERTY(cx, pobj, prop); } return JS_TRUE; } #endif /* JS_HAS_GETTER_SETTER */ #if JS_HAS_OBJ_WATCHPOINT const char js_watch_str[] = "watch"; const char js_unwatch_str[] = "unwatch"; #endif const char js_hasOwnProperty_str[] = "hasOwnProperty"; const char js_isPrototypeOf_str[] = "isPrototypeOf"; const char js_propertyIsEnumerable_str[] = "propertyIsEnumerable"; #if JS_HAS_GETTER_SETTER const char js_defineGetter_str[] = "__defineGetter__"; const char js_defineSetter_str[] = "__defineSetter__"; const char js_lookupGetter_str[] = "__lookupGetter__"; const char js_lookupSetter_str[] = "__lookupSetter__"; #endif static JSFunctionSpec object_methods[] = { #if JS_HAS_TOSOURCE {js_toSource_str, js_obj_toSource, 0, 0, OBJ_TOSTRING_EXTRA}, #endif {js_toString_str, js_obj_toString, 0, 0, OBJ_TOSTRING_EXTRA}, {js_toLocaleString_str, js_obj_toLocaleString, 0, 0, OBJ_TOSTRING_EXTRA}, {js_valueOf_str, obj_valueOf, 0,0,0}, {js_eval_str, obj_eval, 1,0,0}, #if JS_HAS_OBJ_WATCHPOINT {js_watch_str, obj_watch, 2,0,0}, {js_unwatch_str, obj_unwatch, 1,0,0}, #endif {js_hasOwnProperty_str, obj_hasOwnProperty, 1,0,0}, {js_isPrototypeOf_str, obj_isPrototypeOf, 1,0,0}, {js_propertyIsEnumerable_str, obj_propertyIsEnumerable, 1,0,0}, #if JS_HAS_GETTER_SETTER {js_defineGetter_str, obj_defineGetter, 2,0,0}, {js_defineSetter_str, obj_defineSetter, 2,0,0}, {js_lookupGetter_str, obj_lookupGetter, 1,0,0}, {js_lookupSetter_str, obj_lookupSetter, 1,0,0}, #endif {0,0,0,0,0} }; static JSBool Object(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) { if (argc == 0) { /* Trigger logic below to construct a blank object. */ obj = NULL; } else { /* If argv[0] is null or undefined, obj comes back null. */ if (!js_ValueToObject(cx, argv[0], &obj)) return JS_FALSE; } if (!obj) { JS_ASSERT(!argc || JSVAL_IS_NULL(argv[0]) || JSVAL_IS_VOID(argv[0])); if (cx->fp->flags & JSFRAME_CONSTRUCTING) return JS_TRUE; obj = js_NewObject(cx, &js_ObjectClass, NULL, NULL); if (!obj) return JS_FALSE; } *rval = OBJECT_TO_JSVAL(obj); return JS_TRUE; } /* * ObjectOps and Class for with-statement stack objects. */ static JSBool with_LookupProperty(JSContext *cx, JSObject *obj, jsid id, JSObject **objp, JSProperty **propp) { JSObject *proto = OBJ_GET_PROTO(cx, obj); if (!proto) return js_LookupProperty(cx, obj, id, objp, propp); return OBJ_LOOKUP_PROPERTY(cx, proto, id, objp, propp); } static JSBool with_GetProperty(JSContext *cx, JSObject *obj, jsid id, jsval *vp) { JSObject *proto = OBJ_GET_PROTO(cx, obj); if (!proto) return js_GetProperty(cx, obj, id, vp); return OBJ_GET_PROPERTY(cx, proto, id, vp); } static JSBool with_SetProperty(JSContext *cx, JSObject *obj, jsid id, jsval *vp) { JSObject *proto = OBJ_GET_PROTO(cx, obj); if (!proto) return js_SetProperty(cx, obj, id, vp); return OBJ_SET_PROPERTY(cx, proto, id, vp); } static JSBool with_GetAttributes(JSContext *cx, JSObject *obj, jsid id, JSProperty *prop, uintN *attrsp) { JSObject *proto = OBJ_GET_PROTO(cx, obj); if (!proto) return js_GetAttributes(cx, obj, id, prop, attrsp); return OBJ_GET_ATTRIBUTES(cx, proto, id, prop, attrsp); } static JSBool with_SetAttributes(JSContext *cx, JSObject *obj, jsid id, JSProperty *prop, uintN *attrsp) { JSObject *proto = OBJ_GET_PROTO(cx, obj); if (!proto) return js_SetAttributes(cx, obj, id, prop, attrsp); return OBJ_SET_ATTRIBUTES(cx, proto, id, prop, attrsp); } static JSBool with_DeleteProperty(JSContext *cx, JSObject *obj, jsid id, jsval *rval) { JSObject *proto = OBJ_GET_PROTO(cx, obj); if (!proto) return js_DeleteProperty(cx, obj, id, rval); return OBJ_DELETE_PROPERTY(cx, proto, id, rval); } static JSBool with_DefaultValue(JSContext *cx, JSObject *obj, JSType hint, jsval *vp) { JSObject *proto = OBJ_GET_PROTO(cx, obj); if (!proto) return js_DefaultValue(cx, obj, hint, vp); return OBJ_DEFAULT_VALUE(cx, proto, hint, vp); } static JSBool with_Enumerate(JSContext *cx, JSObject *obj, JSIterateOp enum_op, jsval *statep, jsid *idp) { JSObject *proto = OBJ_GET_PROTO(cx, obj); if (!proto) return js_Enumerate(cx, obj, enum_op, statep, idp); return OBJ_ENUMERATE(cx, proto, enum_op, statep, idp); } static JSBool with_CheckAccess(JSContext *cx, JSObject *obj, jsid id, JSAccessMode mode, jsval *vp, uintN *attrsp) { JSObject *proto = OBJ_GET_PROTO(cx, obj); if (!proto) return js_CheckAccess(cx, obj, id, mode, vp, attrsp); return OBJ_CHECK_ACCESS(cx, proto, id, mode, vp, attrsp); } static JSObject * with_ThisObject(JSContext *cx, JSObject *obj) { JSObject *proto = OBJ_GET_PROTO(cx, obj); if (!proto) return obj; return OBJ_THIS_OBJECT(cx, proto); } JS_FRIEND_DATA(JSObjectOps) js_WithObjectOps = { js_NewObjectMap, js_DestroyObjectMap, with_LookupProperty, js_DefineProperty, with_GetProperty, with_SetProperty, with_GetAttributes, with_SetAttributes, with_DeleteProperty, with_DefaultValue, with_Enumerate, with_CheckAccess, with_ThisObject, NATIVE_DROP_PROPERTY, NULL, NULL, NULL, NULL, js_SetProtoOrParent, js_SetProtoOrParent, js_Mark, js_Clear, NULL, NULL }; static JSObjectOps * with_getObjectOps(JSContext *cx, JSClass *clasp) { return &js_WithObjectOps; } JSClass js_WithClass = { "With", JSCLASS_HAS_PRIVATE | JSCLASS_HAS_RESERVED_SLOTS(1) | JSCLASS_IS_ANONYMOUS, JS_PropertyStub, JS_PropertyStub, JS_PropertyStub, JS_PropertyStub, JS_EnumerateStub, JS_ResolveStub, JS_ConvertStub, JS_FinalizeStub, with_getObjectOps, 0,0,0,0,0,0,0 }; JSObject * js_NewWithObject(JSContext *cx, JSObject *proto, JSObject *parent, jsint depth) { JSObject *obj; obj = js_NewObject(cx, &js_WithClass, proto, parent); if (!obj) return NULL; obj->slots[JSSLOT_PRIVATE] = PRIVATE_TO_JSVAL(cx->fp); OBJ_SET_BLOCK_DEPTH(cx, obj, depth); return obj; } JSObject * js_NewBlockObject(JSContext *cx) { JSObject *obj; /* * Null obj's proto slot so that Object.prototype.* does not pollute block * scopes. Make sure obj has its own scope too, since clearing proto does * not affect OBJ_SCOPE(obj). */ obj = js_NewObject(cx, &js_BlockClass, NULL, NULL); if (!obj || !js_GetMutableScope(cx, obj)) return NULL; OBJ_SET_PROTO(cx, obj, NULL); return obj; } JSObject * js_CloneBlockObject(JSContext *cx, JSObject *proto, JSObject *parent, JSStackFrame *fp) { JSObject *clone; clone = js_NewObject(cx, &js_BlockClass, proto, parent); if (!clone) return NULL; clone->slots[JSSLOT_PRIVATE] = PRIVATE_TO_JSVAL(fp); clone->slots[JSSLOT_BLOCK_DEPTH] = OBJ_GET_SLOT(cx, proto, JSSLOT_BLOCK_DEPTH); return clone; } /* * XXXblock this reverses a path in the property tree -- try to share * the prototype's scope harder! */ JSBool js_PutBlockObject(JSContext *cx, JSObject *obj) { JSStackFrame *fp; uintN depth, slot; JSScopeProperty *sprop; fp = (JSStackFrame *) JS_GetPrivate(cx, obj); JS_ASSERT(fp); depth = OBJ_BLOCK_DEPTH(cx, obj); for (sprop = OBJ_SCOPE(obj)->lastProp; sprop; sprop = sprop->parent) { if (sprop->getter != js_BlockClass.getProperty) continue; if (!(sprop->flags & SPROP_HAS_SHORTID)) continue; slot = depth + (uintN)sprop->shortid; JS_ASSERT(slot < fp->script->depth); if (!js_DefineNativeProperty(cx, obj, sprop->id, fp->spbase[slot], NULL, NULL, JSPROP_ENUMERATE | JSPROP_PERMANENT, SPROP_HAS_SHORTID, sprop->shortid, NULL)) { JS_SetPrivate(cx, obj, NULL); return JS_FALSE; } } return JS_SetPrivate(cx, obj, NULL); } static JSBool block_getProperty(JSContext *cx, JSObject *obj, jsval id, jsval *vp) { JSStackFrame *fp; jsint slot; JS_ASSERT(JS_InstanceOf(cx, obj, &js_BlockClass, NULL)); if (!JSVAL_IS_INT(id)) return JS_TRUE; fp = (JSStackFrame *) JS_GetPrivate(cx, obj); if (!fp) return JS_TRUE; slot = OBJ_BLOCK_DEPTH(cx, obj) + (uint16) JSVAL_TO_INT(id); JS_ASSERT((uintN)slot < fp->script->depth); *vp = fp->spbase[slot]; return JS_TRUE; } static JSBool block_setProperty(JSContext *cx, JSObject *obj, jsval id, jsval *vp) { JSStackFrame *fp; jsint slot; JS_ASSERT(JS_InstanceOf(cx, obj, &js_BlockClass, NULL)); if (!JSVAL_IS_INT(id)) return JS_TRUE; fp = (JSStackFrame *) JS_GetPrivate(cx, obj); if (!fp) return JS_TRUE; slot = OBJ_BLOCK_DEPTH(cx, obj) + (uint16) JSVAL_TO_INT(id); JS_ASSERT((uintN)slot < fp->script->depth); fp->spbase[slot] = *vp; return JS_TRUE; } #if JS_HAS_XDR #define NO_PARENT_INDEX (jsatomid)-1 jsatomid FindObjectAtomIndex(JSAtomMap *map, JSObject *obj) { size_t i; JSAtom *atom; for (i = 0; i < map->length; i++) { atom = map->vector[i]; if (ATOM_KEY(atom) == OBJECT_TO_JSVAL(obj)) return i; } return NO_PARENT_INDEX; } static JSBool block_xdrObject(JSXDRState *xdr, JSObject **objp) { JSContext *cx; jsatomid parentId; JSAtomMap *atomMap; JSObject *obj, *parent; uint16 depth, count, i; uint32 tmp; JSTempValueRooter tvr; JSScopeProperty *sprop; jsid propid; JSAtom *atom; int16 shortid; JSBool ok; cx = xdr->cx; #ifdef __GNUC__ obj = NULL; /* quell GCC overwarning */ #endif atomMap = &xdr->script->atomMap; if (xdr->mode == JSXDR_ENCODE) { obj = *objp; parent = OBJ_GET_PARENT(cx, obj); parentId = FindObjectAtomIndex(atomMap, parent); depth = OBJ_BLOCK_DEPTH(cx, obj); count = OBJ_BLOCK_COUNT(cx, obj); tmp = (uint32)(depth << 16) | count; } #ifdef __GNUC__ /* suppress bogus gcc warnings */ else count = 0; #endif /* First, XDR the parent atomid. */ if (!JS_XDRUint32(xdr, &parentId)) return JS_FALSE; if (xdr->mode == JSXDR_DECODE) { obj = js_NewBlockObject(cx); if (!obj) return JS_FALSE; *objp = obj; /* * If there's a parent id, then get the parent out of our script's * atomMap. We know that we XDR block object in outer-to-inner order, * which means that getting the parent now will work. */ if (parentId == NO_PARENT_INDEX) { parent = NULL; } else { atom = js_GetAtom(cx, atomMap, parentId); JS_ASSERT(ATOM_IS_OBJECT(atom)); parent = ATOM_TO_OBJECT(atom); } obj->slots[JSSLOT_PARENT] = OBJECT_TO_JSVAL(parent); } JS_PUSH_SINGLE_TEMP_ROOT(cx, OBJECT_TO_JSVAL(obj), &tvr); if (!JS_XDRUint32(xdr, &tmp)) { JS_POP_TEMP_ROOT(cx, &tvr); return JS_FALSE; } if (xdr->mode == JSXDR_DECODE) { depth = (uint16)(tmp >> 16); count = (uint16)tmp; obj->slots[JSSLOT_BLOCK_DEPTH] = INT_TO_JSVAL(depth); } /* * XDR the block object's properties. We know that there are 'count' * properties to XDR, stored as id/shortid pairs. We do not XDR any * non-native properties, only those that the compiler created. */ sprop = NULL; ok = JS_TRUE; for (i = 0; i < count; i++) { if (xdr->mode == JSXDR_ENCODE) { /* Find a property to XDR. */ do { /* If sprop is NULL, this is the first property. */ sprop = sprop ? sprop->parent : OBJ_SCOPE(obj)->lastProp; } while (!(sprop->flags & SPROP_HAS_SHORTID)); JS_ASSERT(sprop->getter == js_BlockClass.getProperty); propid = sprop->id; JS_ASSERT(JSID_IS_ATOM(propid)); atom = JSID_TO_ATOM(propid); shortid = sprop->shortid; JS_ASSERT(shortid >= 0); } /* XDR the real id, then the shortid. */ if (!js_XDRStringAtom(xdr, &atom) || !JS_XDRUint16(xdr, (uint16 *)&shortid)) { ok = JS_FALSE; break; } if (xdr->mode == JSXDR_DECODE) { if (!js_DefineNativeProperty(cx, obj, ATOM_TO_JSID(atom), JSVAL_VOID, NULL, NULL, JSPROP_ENUMERATE | JSPROP_PERMANENT, SPROP_HAS_SHORTID, shortid, NULL)) { ok = JS_FALSE; break; } } } JS_POP_TEMP_ROOT(cx, &tvr); return ok; } #else # define block_xdrObject NULL #endif JSClass js_BlockClass = { "Block", JSCLASS_HAS_PRIVATE | JSCLASS_HAS_RESERVED_SLOTS(1) | JSCLASS_IS_ANONYMOUS | JSCLASS_HAS_CACHED_PROTO(JSProto_Block), JS_PropertyStub, JS_PropertyStub, block_getProperty, block_setProperty, JS_EnumerateStub, JS_ResolveStub, JS_ConvertStub, JS_FinalizeStub, NULL, NULL, NULL, NULL, block_xdrObject, NULL, NULL, NULL }; JSObject* js_InitBlockClass(JSContext *cx, JSObject* obj) { JSObject *proto; proto = JS_InitClass(cx, obj, NULL, &js_BlockClass, NULL, 0, NULL, NULL, NULL, NULL); if (!proto) return NULL; OBJ_SET_PROTO(cx, proto, NULL); return proto; } JSObject * js_InitObjectClass(JSContext *cx, JSObject *obj) { JSObject *proto; jsval eval; proto = JS_InitClass(cx, obj, NULL, &js_ObjectClass, Object, 1, object_props, object_methods, NULL, NULL); if (!proto) return NULL; /* ECMA (15.1.2.1) says 'eval' is also a property of the global object. */ if (!OBJ_GET_PROPERTY(cx, proto, ATOM_TO_JSID(cx->runtime->atomState.evalAtom), &eval)) { return NULL; } if (!OBJ_DEFINE_PROPERTY(cx, obj, ATOM_TO_JSID(cx->runtime->atomState.evalAtom), eval, NULL, NULL, 0, NULL)) { return NULL; } return proto; } void js_InitObjectMap(JSObjectMap *map, jsrefcount nrefs, JSObjectOps *ops, JSClass *clasp) { map->nrefs = nrefs; map->ops = ops; map->nslots = JS_INITIAL_NSLOTS; map->freeslot = JSSLOT_FREE(clasp); } JSObjectMap * js_NewObjectMap(JSContext *cx, jsrefcount nrefs, JSObjectOps *ops, JSClass *clasp, JSObject *obj) { return (JSObjectMap *) js_NewScope(cx, nrefs, ops, clasp, obj); } void js_DestroyObjectMap(JSContext *cx, JSObjectMap *map) { js_DestroyScope(cx, (JSScope *)map); } JSObjectMap * js_HoldObjectMap(JSContext *cx, JSObjectMap *map) { JS_ASSERT(map->nrefs >= 0); JS_ATOMIC_INCREMENT(&map->nrefs); return map; } JSObjectMap * js_DropObjectMap(JSContext *cx, JSObjectMap *map, JSObject *obj) { JS_ASSERT(map->nrefs > 0); JS_ATOMIC_DECREMENT(&map->nrefs); if (map->nrefs == 0) { map->ops->destroyObjectMap(cx, map); return NULL; } if (MAP_IS_NATIVE(map) && ((JSScope *)map)->object == obj) ((JSScope *)map)->object = NULL; return map; } static jsval * AllocSlots(JSContext *cx, jsval *slots, uint32 nslots) { size_t nbytes, obytes, minbytes; uint32 i, oslots; jsval *newslots; nbytes = (nslots + 1) * sizeof(jsval); if (slots) { oslots = slots[-1]; obytes = (oslots + 1) * sizeof(jsval); } else { oslots = 0; obytes = 0; } if (nbytes <= GC_NBYTES_MAX) { newslots = (jsval *) js_NewGCThing(cx, GCX_PRIVATE, nbytes); } else { newslots = (jsval *) JS_realloc(cx, (obytes <= GC_NBYTES_MAX) ? NULL : slots - 1, nbytes); } if (!newslots) return NULL; if (obytes != 0) { /* If either nbytes or obytes fit in a GC-thing, we must copy. */ minbytes = JS_MIN(nbytes, obytes); if (minbytes <= GC_NBYTES_MAX) memcpy(newslots + 1, slots, minbytes - sizeof(jsval)); /* If nbytes are in a GC-thing but obytes aren't, free obytes. */ if (nbytes <= GC_NBYTES_MAX && obytes > GC_NBYTES_MAX) JS_free(cx, slots - 1); /* If we're extending an allocation, initialize free slots. */ if (nslots > oslots) { for (i = 1 + oslots; i <= nslots; i++) newslots[i] = JSVAL_VOID; } } newslots[0] = nslots; return ++newslots; } static void FreeSlots(JSContext *cx, jsval *slots) { size_t nbytes; /* * NB: We count on smaller GC-things being finalized before larger things * that become garbage during the same GC. Without this assumption, we * couldn't load slots[-1] here without possibly loading a gcFreeList link * (see struct JSGCThing in jsgc.h). */ nbytes = (slots[-1] + 1) * sizeof(jsval); if (nbytes > GC_NBYTES_MAX) JS_free(cx, slots - 1); } extern JSBool js_GetClassId(JSContext *cx, JSClass *clasp, jsid *idp) { JSProtoKey key; JSAtom *atom; key = JSCLASS_CACHED_PROTO_KEY(clasp); if (key != JSProto_Null) { *idp = INT_TO_JSID(key); } else if (clasp->flags & JSCLASS_IS_ANONYMOUS) { *idp = INT_TO_JSID(JSProto_Object); } else { atom = js_Atomize(cx, clasp->name, strlen(clasp->name), 0); if (!atom) return JS_FALSE; *idp = ATOM_TO_JSID(atom); } return JS_TRUE; } JSObject * js_NewObject(JSContext *cx, JSClass *clasp, JSObject *proto, JSObject *parent) { jsid id; JSObject *obj; JSObjectOps *ops; JSObjectMap *map; JSClass *protoclasp; uint32 nslots, i; jsval *newslots; JSTempValueRooter tvr; /* Bootstrap the ur-object, and make it the default prototype object. */ if (!proto) { if (!js_GetClassId(cx, clasp, &id)) return NULL; if (!js_GetClassPrototype(cx, parent, id, &proto)) return NULL; if (!proto && !js_GetClassPrototype(cx, parent, INT_TO_JSID(JSProto_Object), &proto)) { return NULL; } } /* Always call the class's getObjectOps hook if it has one. */ ops = clasp->getObjectOps ? clasp->getObjectOps(cx, clasp) : &js_ObjectOps; /* * Allocate a zeroed object from the GC heap. Do this *after* any other * GC-thing allocations under js_GetClassPrototype or clasp->getObjectOps, * to avoid displacing the newborn root for obj. */ obj = (JSObject *) js_NewGCThing(cx, GCX_OBJECT, sizeof(JSObject)); if (!obj) return NULL; /* * Root obj to prevent it from being collected out from under this call. * to js_NewObject. AllocSlots can trigger a finalizer from a last-ditch * GC calling JS_ClearNewbornRoots. There's also the possibilty of things * happening under the objectHook call-out further below. */ JS_PUSH_TEMP_ROOT_OBJECT(cx, obj, &tvr); /* * Share proto's map only if it has the same JSObjectOps, and only if * proto's class has the same private and reserved slots as obj's map * and class have. We assume that if prototype and object are of the * same class, they always have the same number of computed reserved * slots (returned via clasp->reserveSlots); otherwise, prototype and * object classes must have the same (null or not) reserveSlots hook. */ if (proto && (map = proto->map)->ops == ops && ((protoclasp = OBJ_GET_CLASS(cx, proto)) == clasp || (!((protoclasp->flags ^ clasp->flags) & (JSCLASS_HAS_PRIVATE | (JSCLASS_RESERVED_SLOTS_MASK << JSCLASS_RESERVED_SLOTS_SHIFT))) && protoclasp->reserveSlots == clasp->reserveSlots))) { /* * Default parent to the parent of the prototype, which was set from * the parent of the prototype's constructor. */ if (!parent) parent = OBJ_GET_PARENT(cx, proto); /* Share the given prototype's map. */ obj->map = js_HoldObjectMap(cx, map); /* Ensure that obj starts with the minimum slots for clasp. */ nslots = JS_INITIAL_NSLOTS; } else { /* Leave parent alone. Allocate a new map for obj. */ map = ops->newObjectMap(cx, 1, ops, clasp, obj); if (!map) goto bad; obj->map = map; /* Let ops->newObjectMap set nslots so as to reserve slots. */ nslots = map->nslots; } /* Allocate a slots vector, with a -1'st element telling its length. */ newslots = AllocSlots(cx, NULL, nslots); if (!newslots) { js_DropObjectMap(cx, obj->map, obj); obj->map = NULL; goto bad; } /* Set the proto, parent, and class properties. */ newslots[JSSLOT_PROTO] = OBJECT_TO_JSVAL(proto); newslots[JSSLOT_PARENT] = OBJECT_TO_JSVAL(parent); newslots[JSSLOT_CLASS] = PRIVATE_TO_JSVAL(clasp); /* Clear above JSSLOT_CLASS so the GC doesn't load uninitialized memory. */ for (i = JSSLOT_CLASS + 1; i < nslots; i++) newslots[i] = JSVAL_VOID; /* Store newslots after initializing all of 'em, just in case. */ obj->slots = newslots; if (cx->runtime->objectHook) { JS_KEEP_ATOMS(cx->runtime); cx->runtime->objectHook(cx, obj, JS_TRUE, cx->runtime->objectHookData); JS_UNKEEP_ATOMS(cx->runtime); } out: JS_POP_TEMP_ROOT(cx, &tvr); cx->weakRoots.newborn[GCX_OBJECT] = (JSGCThing *) obj; return obj; bad: obj = NULL; goto out; } JS_STATIC_DLL_CALLBACK(JSObject *) js_InitNullClass(JSContext *cx, JSObject *obj) { JS_ASSERT(0); return NULL; } #define JS_PROTO(name,code,init) extern JSObject *init(JSContext *, JSObject *); #include "jsproto.tbl" #undef JS_PROTO static JSObjectOp lazy_prototype_init[JSProto_LIMIT] = { #define JS_PROTO(name,code,init) init, #include "jsproto.tbl" #undef JS_PROTO }; JSBool js_GetClassObject(JSContext *cx, JSObject *obj, JSProtoKey key, JSObject **objp) { JSBool ok; JSObject *tmp, *cobj; JSResolvingKey rkey; JSResolvingEntry *rentry; uint32 generation; JSObjectOp init; jsval v; while ((tmp = OBJ_GET_PARENT(cx, obj)) != NULL) obj = tmp; if (!(OBJ_GET_CLASS(cx, obj)->flags & JSCLASS_IS_GLOBAL)) { *objp = NULL; return JS_TRUE; } ok = JS_GetReservedSlot(cx, obj, key, &v); if (!ok) return JS_FALSE; if (!JSVAL_IS_PRIMITIVE(v)) { *objp = JSVAL_TO_OBJECT(v); return JS_TRUE; } rkey.obj = obj; rkey.id = ATOM_TO_JSID(cx->runtime->atomState.classAtoms[key]); if (!js_StartResolving(cx, &rkey, JSRESFLAG_LOOKUP, &rentry)) return JS_FALSE; if (!rentry) { /* Already caching key in obj -- suppress recursion. */ *objp = NULL; return JS_TRUE; } generation = cx->resolvingTable->generation; cobj = NULL; init = lazy_prototype_init[key]; if (init) { if (!init(cx, obj)) { ok = JS_FALSE; } else { ok = JS_GetReservedSlot(cx, obj, key, &v); if (ok && !JSVAL_IS_PRIMITIVE(v)) cobj = JSVAL_TO_OBJECT(v); } } js_StopResolving(cx, &rkey, JSRESFLAG_LOOKUP, rentry, generation); *objp = cobj; return ok; } JSBool js_SetClassObject(JSContext *cx, JSObject *obj, JSProtoKey key, JSObject *cobj) { JS_ASSERT(!OBJ_GET_PARENT(cx, obj)); if (!(OBJ_GET_CLASS(cx, obj)->flags & JSCLASS_IS_GLOBAL)) return JS_TRUE; return JS_SetReservedSlot(cx, obj, key, OBJECT_TO_JSVAL(cobj)); } JSBool js_FindClassObject(JSContext *cx, JSObject *start, jsid id, jsval *vp) { JSObject *obj, *cobj, *pobj; JSProtoKey key; JSProperty *prop; JSScopeProperty *sprop; if (start || (cx->fp && (start = cx->fp->scopeChain) != NULL)) { /* Find the topmost object in the scope chain. */ do { obj = start; start = OBJ_GET_PARENT(cx, obj); } while (start); } else { obj = cx->globalObject; if (!obj) { *vp = JSVAL_VOID; return JS_TRUE; } } OBJ_TO_INNER_OBJECT(cx, obj); if (!obj) return JS_FALSE; if (JSID_IS_INT(id)) { key = JSID_TO_INT(id); JS_ASSERT(key != JSProto_Null); if (!js_GetClassObject(cx, obj, key, &cobj)) return JS_FALSE; if (cobj) { *vp = OBJECT_TO_JSVAL(cobj); return JS_TRUE; } id = ATOM_TO_JSID(cx->runtime->atomState.classAtoms[key]); } JS_ASSERT(OBJ_IS_NATIVE(obj)); if (!js_LookupPropertyWithFlags(cx, obj, id, JSRESOLVE_CLASSNAME, &pobj, &prop)) { return JS_FALSE; } if (!prop) { *vp = JSVAL_VOID; return JS_TRUE; } JS_ASSERT(OBJ_IS_NATIVE(pobj)); sprop = (JSScopeProperty *) prop; JS_ASSERT(SPROP_HAS_VALID_SLOT(sprop, OBJ_SCOPE(pobj))); *vp = OBJ_GET_SLOT(cx, pobj, sprop->slot); OBJ_DROP_PROPERTY(cx, pobj, prop); return JS_TRUE; } JSObject * js_ConstructObject(JSContext *cx, JSClass *clasp, JSObject *proto, JSObject *parent, uintN argc, jsval *argv) { jsid id; jsval cval, rval; JSTempValueRooter argtvr, tvr; JSObject *obj, *ctor; JS_PUSH_TEMP_ROOT(cx, argc, argv, &argtvr); if (!js_GetClassId(cx, clasp, &id) || !js_FindClassObject(cx, parent, id, &cval)) { JS_POP_TEMP_ROOT(cx, &argtvr); return NULL; } if (JSVAL_IS_PRIMITIVE(cval)) { js_ReportIsNotFunction(cx, &cval, JSV2F_CONSTRUCT | JSV2F_SEARCH_STACK); JS_POP_TEMP_ROOT(cx, &argtvr); return NULL; } /* * Protect cval in case a crazy getter for .prototype uproots it. After * this point, all control flow must exit through label out with obj set. */ JS_PUSH_SINGLE_TEMP_ROOT(cx, cval, &tvr); /* * If proto or parent are NULL, set them to Constructor.prototype and/or * Constructor.__parent__, just like JSOP_NEW does. */ ctor = JSVAL_TO_OBJECT(cval); if (!parent) parent = OBJ_GET_PARENT(cx, ctor); if (!proto) { if (!OBJ_GET_PROPERTY(cx, ctor, ATOM_TO_JSID(cx->runtime->atomState .classPrototypeAtom), &rval)) { obj = NULL; goto out; } if (JSVAL_IS_OBJECT(rval)) proto = JSVAL_TO_OBJECT(rval); } obj = js_NewObject(cx, clasp, proto, parent); if (!obj) goto out; if (!js_InternalConstruct(cx, obj, cval, argc, argv, &rval)) goto bad; if (JSVAL_IS_PRIMITIVE(rval)) goto out; obj = JSVAL_TO_OBJECT(rval); /* * If the instance's class differs from what was requested, throw a type * error. If the given class has both the JSCLASS_HAS_PRIVATE and the * JSCLASS_CONSTRUCT_PROTOTYPE flags, and the instance does not have its * private data set at this point, then the constructor was replaced and * we should throw a type error. */ if (OBJ_GET_CLASS(cx, obj) != clasp || (!(~clasp->flags & (JSCLASS_HAS_PRIVATE | JSCLASS_CONSTRUCT_PROTOTYPE)) && !JS_GetPrivate(cx, obj))) { JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_WRONG_CONSTRUCTOR, clasp->name); goto bad; } out: JS_POP_TEMP_ROOT(cx, &tvr); JS_POP_TEMP_ROOT(cx, &argtvr); return obj; bad: cx->weakRoots.newborn[GCX_OBJECT] = NULL; obj = NULL; goto out; } void js_FinalizeObject(JSContext *cx, JSObject *obj) { JSObjectMap *map; /* Cope with stillborn objects that have no map. */ map = obj->map; if (!map) return; JS_ASSERT(obj->slots); if (cx->runtime->objectHook) cx->runtime->objectHook(cx, obj, JS_FALSE, cx->runtime->objectHookData); /* Remove all watchpoints with weak links to obj. */ JS_ClearWatchPointsForObject(cx, obj); /* * Finalize obj first, in case it needs map and slots. Optimized to use * LOCKED_OBJ_GET_CLASS instead of OBJ_GET_CLASS, so we avoid "promoting" * obj's scope from lock-free to lock-full (see jslock.c:ClaimScope) when * we're called from the GC. Only the GC should call js_FinalizeObject, * and no other threads run JS (and possibly racing to update obj->slots) * while the GC is running. */ LOCKED_OBJ_GET_CLASS(obj)->finalize(cx, obj); /* Drop map and free slots. */ js_DropObjectMap(cx, map, obj); obj->map = NULL; FreeSlots(cx, obj->slots); obj->slots = NULL; } /* XXXbe if one adds props, deletes earlier props, adds more, the last added won't recycle the deleted props' slots. */ JSBool js_AllocSlot(JSContext *cx, JSObject *obj, uint32 *slotp) { JSObjectMap *map; JSClass *clasp; uint32 nslots; jsval *newslots; map = obj->map; JS_ASSERT(!MAP_IS_NATIVE(map) || ((JSScope *)map)->object == obj); clasp = LOCKED_OBJ_GET_CLASS(obj); if (map->freeslot == JSSLOT_FREE(clasp)) { /* Adjust map->freeslot to include computed reserved slots, if any. */ if (clasp->reserveSlots) map->freeslot += clasp->reserveSlots(cx, obj); } nslots = map->nslots; if (map->freeslot >= nslots) { nslots = map->freeslot; JS_ASSERT(nslots >= JS_INITIAL_NSLOTS); nslots += (nslots + 1) / 2; newslots = AllocSlots(cx, obj->slots, nslots); if (!newslots) return JS_FALSE; map->nslots = nslots; obj->slots = newslots; } #ifdef TOO_MUCH_GC obj->slots[map->freeslot] = JSVAL_VOID; #endif *slotp = map->freeslot++; return JS_TRUE; } void js_FreeSlot(JSContext *cx, JSObject *obj, uint32 slot) { JSObjectMap *map; uint32 nslots; jsval *newslots; OBJ_CHECK_SLOT(obj, slot); obj->slots[slot] = JSVAL_VOID; map = obj->map; JS_ASSERT(!MAP_IS_NATIVE(map) || ((JSScope *)map)->object == obj); if (map->freeslot == slot + 1) map->freeslot = slot; nslots = map->nslots; if (nslots > JS_INITIAL_NSLOTS && map->freeslot < nslots / 2) { nslots = map->freeslot; nslots += nslots / 2; if (nslots < JS_INITIAL_NSLOTS) nslots = JS_INITIAL_NSLOTS; newslots = AllocSlots(cx, obj->slots, nslots); if (!newslots) return; map->nslots = nslots; obj->slots = newslots; } } /* JSVAL_INT_MAX as a string */ #define JSVAL_INT_MAX_STRING "1073741823" #define CHECK_FOR_STRING_INDEX(id) \ JS_BEGIN_MACRO \ if (JSID_IS_ATOM(id)) { \ JSAtom *atom_ = JSID_TO_ATOM(id); \ JSString *str_ = ATOM_TO_STRING(atom_); \ const jschar *cp_ = str_->chars; \ JSBool negative_ = (*cp_ == '-'); \ if (negative_) cp_++; \ if (JS7_ISDEC(*cp_)) { \ size_t n_ = str_->length - negative_; \ if (n_ <= sizeof(JSVAL_INT_MAX_STRING) - 1) \ id = CheckForStringIndex(id, cp_, cp_ + n_, negative_); \ } \ } \ JS_END_MACRO static jsid CheckForStringIndex(jsid id, const jschar *cp, const jschar *end, JSBool negative) { jsuint index = JS7_UNDEC(*cp++); jsuint oldIndex = 0; jsuint c = 0; if (index != 0) { while (JS7_ISDEC(*cp)) { oldIndex = index; c = JS7_UNDEC(*cp); index = 10 * index + c; cp++; } } if (cp == end && (oldIndex < (JSVAL_INT_MAX / 10) || (oldIndex == (JSVAL_INT_MAX / 10) && c <= (JSVAL_INT_MAX % 10)))) { if (negative) index = 0 - index; id = INT_TO_JSID((jsint)index); } return id; } static JSBool HidePropertyName(JSContext *cx, jsid *idp) { jsid id; JSAtom *atom, *hidden; id = *idp; JS_ASSERT(JSID_IS_ATOM(id)); atom = JSID_TO_ATOM(id); JS_ASSERT(!(atom->flags & ATOM_HIDDEN)); JS_ASSERT(ATOM_IS_STRING(atom)); hidden = js_AtomizeString(cx, ATOM_TO_STRING(atom), ATOM_HIDDEN); if (!hidden) return JS_FALSE; /* * Link hidden to unhidden atom to optimize call_enumerate -- this means * the GC must mark a hidden atom's unhidden counterpart (see js_MarkAtom * in jsgc.c). It uses the atom's entry.value member for this linkage. */ hidden->entry.value = atom; *idp = ATOM_TO_JSID(hidden); return JS_TRUE; } JSScopeProperty * js_AddHiddenProperty(JSContext *cx, JSObject *obj, jsid id, JSPropertyOp getter, JSPropertyOp setter, uint32 slot, uintN attrs, uintN flags, intN shortid) { if (!HidePropertyName(cx, &id)) return NULL; flags |= SPROP_IS_HIDDEN; return js_AddNativeProperty(cx, obj, id, getter, setter, slot, attrs, flags, shortid); } JSBool js_LookupHiddenProperty(JSContext *cx, JSObject *obj, jsid id, JSObject **objp, JSProperty **propp) { return HidePropertyName(cx, &id) && js_LookupPropertyWithFlags(cx, obj, id, JSRESOLVE_HIDDEN, objp, propp); } JSScopeProperty * js_AddNativeProperty(JSContext *cx, JSObject *obj, jsid id, JSPropertyOp getter, JSPropertyOp setter, uint32 slot, uintN attrs, uintN flags, intN shortid) { JSScope *scope; JSScopeProperty *sprop; JS_LOCK_OBJ(cx, obj); scope = js_GetMutableScope(cx, obj); if (!scope) { sprop = NULL; } else { /* * Handle old bug that took empty string as zero index. Also convert * string indices to integers if appropriate. */ CHECK_FOR_STRING_INDEX(id); sprop = js_AddScopeProperty(cx, scope, id, getter, setter, slot, attrs, flags, shortid); } JS_UNLOCK_OBJ(cx, obj); return sprop; } JSScopeProperty * js_ChangeNativePropertyAttrs(JSContext *cx, JSObject *obj, JSScopeProperty *sprop, uintN attrs, uintN mask, JSPropertyOp getter, JSPropertyOp setter) { JSScope *scope; JS_LOCK_OBJ(cx, obj); scope = js_GetMutableScope(cx, obj); if (!scope) { sprop = NULL; } else { sprop = js_ChangeScopePropertyAttrs(cx, scope, sprop, attrs, mask, getter, setter); if (sprop) { PROPERTY_CACHE_FILL(&cx->runtime->propertyCache, obj, sprop->id, sprop); } } JS_UNLOCK_OBJ(cx, obj); return sprop; } JSBool js_DefineProperty(JSContext *cx, JSObject *obj, jsid id, jsval value, JSPropertyOp getter, JSPropertyOp setter, uintN attrs, JSProperty **propp) { return js_DefineNativeProperty(cx, obj, id, value, getter, setter, attrs, 0, 0, propp); } /* * Backward compatibility requires allowing addProperty hooks to mutate the * nominal initial value of a slot-full property, while GC safety wants that * value to be stored before the call-out through the hook. Optimize to do * both while saving cycles for classes that stub their addProperty hook. */ #define ADD_PROPERTY_HELPER(cx,clasp,obj,scope,sprop,vp,cleanup) \ JS_BEGIN_MACRO \ if ((clasp)->addProperty != JS_PropertyStub) { \ jsval nominal_ = *(vp); \ if (!(clasp)->addProperty(cx, obj, SPROP_USERID(sprop), vp)) { \ cleanup; \ } \ if (*(vp) != nominal_) { \ if (SPROP_HAS_VALID_SLOT(sprop, scope)) \ LOCKED_OBJ_SET_SLOT(obj, (sprop)->slot, *(vp)); \ } \ } \ JS_END_MACRO JSBool js_DefineNativeProperty(JSContext *cx, JSObject *obj, jsid id, jsval value, JSPropertyOp getter, JSPropertyOp setter, uintN attrs, uintN flags, intN shortid, JSProperty **propp) { JSClass *clasp; JSScope *scope; JSScopeProperty *sprop; /* * Handle old bug that took empty string as zero index. Also convert * string indices to integers if appropriate. */ CHECK_FOR_STRING_INDEX(id); #if JS_HAS_GETTER_SETTER /* * If defining a getter or setter, we must check for its counterpart and * update the attributes and property ops. A getter or setter is really * only half of a property. */ if (attrs & (JSPROP_GETTER | JSPROP_SETTER)) { JSObject *pobj; JSProperty *prop; /* * If JS_THREADSAFE and id is found, js_LookupProperty returns with * sprop non-null and pobj locked. If pobj == obj, the property is * already in obj and obj has its own (mutable) scope. So if we are * defining a getter whose setter was already defined, or vice versa, * finish the job via js_ChangeScopePropertyAttributes, and refresh * the property cache line for (obj, id) to map sprop. */ if (!js_LookupProperty(cx, obj, id, &pobj, &prop)) return JS_FALSE; sprop = (JSScopeProperty *) prop; if (sprop && pobj == obj && (sprop->attrs & (JSPROP_GETTER | JSPROP_SETTER))) { sprop = js_ChangeScopePropertyAttrs(cx, OBJ_SCOPE(obj), sprop, attrs, sprop->attrs, (attrs & JSPROP_GETTER) ? getter : sprop->getter, (attrs & JSPROP_SETTER) ? setter : sprop->setter); /* NB: obj == pobj, so we can share unlock code at the bottom. */ if (!sprop) goto bad; goto out; } if (prop) { /* NB: call OBJ_DROP_PROPERTY, as pobj might not be native. */ OBJ_DROP_PROPERTY(cx, pobj, prop); prop = NULL; } } #endif /* JS_HAS_GETTER_SETTER */ /* Lock if object locking is required by this implementation. */ JS_LOCK_OBJ(cx, obj); /* Use the object's class getter and setter by default. */ clasp = LOCKED_OBJ_GET_CLASS(obj); if (!getter) getter = clasp->getProperty; if (!setter) setter = clasp->setProperty; /* Get obj's own scope if it has one, or create a new one for obj. */ scope = js_GetMutableScope(cx, obj); if (!scope) goto bad; /* Add the property to scope, or replace an existing one of the same id. */ if (clasp->flags & JSCLASS_SHARE_ALL_PROPERTIES) attrs |= JSPROP_SHARED; sprop = js_AddScopeProperty(cx, scope, id, getter, setter, SPROP_INVALID_SLOT, attrs, flags, shortid); if (!sprop) goto bad; /* Store value before calling addProperty, in case the latter GC's. */ if (SPROP_HAS_VALID_SLOT(sprop, scope)) LOCKED_OBJ_SET_SLOT(obj, sprop->slot, value); /* XXXbe called with lock held */ ADD_PROPERTY_HELPER(cx, clasp, obj, scope, sprop, &value, js_RemoveScopeProperty(cx, scope, id); goto bad); #if JS_HAS_GETTER_SETTER out: #endif PROPERTY_CACHE_FILL(&cx->runtime->propertyCache, obj, id, sprop); if (propp) *propp = (JSProperty *) sprop; else JS_UNLOCK_OBJ(cx, obj); return JS_TRUE; bad: JS_UNLOCK_OBJ(cx, obj); return JS_FALSE; } /* * Given pc pointing after a property accessing bytecode, return true if the * access is "object-detecting" in the sense used by web scripts, e.g., when * checking whether document.all is defined. */ static JSBool Detecting(JSContext *cx, jsbytecode *pc) { JSScript *script; jsbytecode *endpc; JSOp op; JSAtom *atom; if (!cx->fp) return JS_FALSE; script = cx->fp->script; for (endpc = script->code + script->length; pc < endpc; pc++) { /* General case: a branch or equality op follows the access. */ op = (JSOp) *pc; if (js_CodeSpec[op].format & JOF_DETECTING) return JS_TRUE; /* * Special case #1: handle (document.all == null). Don't sweat about * JS1.2's revision of the equality operators here. */ if (op == JSOP_NULL) { if (++pc < endpc) return *pc == JSOP_EQ || *pc == JSOP_NE; break; } /* * Special case #2: handle (document.all == undefined). Don't worry * about someone redefining undefined, which was added by Edition 3, * so is read/write for backward compatibility. */ if (op == JSOP_NAME) { atom = GET_ATOM(cx, script, pc); if (atom == cx->runtime->atomState.typeAtoms[JSTYPE_VOID] && (pc += js_CodeSpec[op].length) < endpc) { op = (JSOp) *pc; return op == JSOP_EQ || op == JSOP_NE || op == JSOP_NEW_EQ || op == JSOP_NEW_NE; } break; } /* At this point, anything but grouping means we're not detecting. */ if (op != JSOP_GROUP) break; } return JS_FALSE; } JS_FRIEND_API(JSBool) js_LookupProperty(JSContext *cx, JSObject *obj, jsid id, JSObject **objp, JSProperty **propp) { return js_LookupPropertyWithFlags(cx, obj, id, 0, objp, propp); } JSBool js_LookupPropertyWithFlags(JSContext *cx, JSObject *obj, jsid id, uintN flags, JSObject **objp, JSProperty **propp) { JSObject *start, *obj2, *proto; JSScope *scope; JSScopeProperty *sprop; JSClass *clasp; JSResolveOp resolve; JSResolvingKey key; JSResolvingEntry *entry; uint32 generation; JSNewResolveOp newresolve; jsbytecode *pc; const JSCodeSpec *cs; uint32 format; JSBool ok; /* * Handle old bug that took empty string as zero index. Also convert * string indices to integers if appropriate. */ CHECK_FOR_STRING_INDEX(id); /* Search scopes starting with obj and following the prototype link. */ start = obj; for (;;) { JS_LOCK_OBJ(cx, obj); scope = OBJ_SCOPE(obj); if (scope->object == obj) { sprop = SCOPE_GET_PROPERTY(scope, id); } else { /* Shared prototype scope: try resolve before lookup. */ sprop = NULL; } /* Try obj's class resolve hook if id was not found in obj's scope. */ if (!sprop) { clasp = LOCKED_OBJ_GET_CLASS(obj); resolve = clasp->resolve; if (resolve != JS_ResolveStub) { /* Avoid recursion on (obj, id) already being resolved on cx. */ key.obj = obj; key.id = id; /* * Once we have successfully added an entry for (obj, key) to * cx->resolvingTable, control must go through cleanup: before * returning. But note that JS_DHASH_ADD may find an existing * entry, in which case we bail to suppress runaway recursion. */ if (!js_StartResolving(cx, &key, JSRESFLAG_LOOKUP, &entry)) { JS_UNLOCK_OBJ(cx, obj); return JS_FALSE; } if (!entry) { /* Already resolving id in obj -- suppress recursion. */ JS_UNLOCK_OBJ(cx, obj); goto out; } generation = cx->resolvingTable->generation; /* Null *propp here so we can test it at cleanup: safely. */ *propp = NULL; if (clasp->flags & JSCLASS_NEW_RESOLVE) { newresolve = (JSNewResolveOp)resolve; if (!(flags & JSRESOLVE_CLASSNAME) && cx->fp && (pc = cx->fp->pc)) { cs = &js_CodeSpec[*pc]; format = cs->format; if ((format & JOF_MODEMASK) != JOF_NAME) flags |= JSRESOLVE_QUALIFIED; if ((format & JOF_ASSIGNING) || (cx->fp->flags & JSFRAME_ASSIGNING)) { flags |= JSRESOLVE_ASSIGNING; } else { pc += cs->length; if (Detecting(cx, pc)) flags |= JSRESOLVE_DETECTING; } if (format & JOF_DECLARING) flags |= JSRESOLVE_DECLARING; } obj2 = (clasp->flags & JSCLASS_NEW_RESOLVE_GETS_START) ? start : NULL; JS_UNLOCK_OBJ(cx, obj); /* Protect id and all atoms from a GC nested in resolve. */ JS_KEEP_ATOMS(cx->runtime); ok = newresolve(cx, obj, ID_TO_VALUE(id), flags, &obj2); JS_UNKEEP_ATOMS(cx->runtime); if (!ok) goto cleanup; JS_LOCK_OBJ(cx, obj); if (obj2) { /* Resolved: juggle locks and lookup id again. */ if (obj2 != obj) { JS_UNLOCK_OBJ(cx, obj); JS_LOCK_OBJ(cx, obj2); } scope = OBJ_SCOPE(obj2); if (!MAP_IS_NATIVE(&scope->map)) { /* Whoops, newresolve handed back a foreign obj2. */ JS_ASSERT(obj2 != obj); JS_UNLOCK_OBJ(cx, obj2); ok = OBJ_LOOKUP_PROPERTY(cx, obj2, id, objp, propp); if (!ok || *propp) goto cleanup; JS_LOCK_OBJ(cx, obj2); } else { /* * Require that obj2 have its own scope now, as we * do for old-style resolve. If it doesn't, then * id was not truly resolved, and we'll find it in * the proto chain, or miss it if obj2's proto is * not on obj's proto chain. That last case is a * "too bad!" case. */ if (scope->object == obj2) sprop = SCOPE_GET_PROPERTY(scope, id); } if (sprop) { JS_ASSERT(obj2 == scope->object); obj = obj2; } else if (obj2 != obj) { JS_UNLOCK_OBJ(cx, obj2); JS_LOCK_OBJ(cx, obj); } } } else { /* * Old resolve always requires id re-lookup if obj owns * its scope after resolve returns. */ JS_UNLOCK_OBJ(cx, obj); ok = resolve(cx, obj, ID_TO_VALUE(id)); if (!ok) goto cleanup; JS_LOCK_OBJ(cx, obj); scope = OBJ_SCOPE(obj); JS_ASSERT(MAP_IS_NATIVE(&scope->map)); if (scope->object == obj) sprop = SCOPE_GET_PROPERTY(scope, id); } cleanup: js_StopResolving(cx, &key, JSRESFLAG_LOOKUP, entry, generation); if (!ok || *propp) return ok; } } if (sprop) { JS_ASSERT(OBJ_SCOPE(obj) == scope); *objp = scope->object; /* XXXbe hide in jsscope.[ch] */ *propp = (JSProperty *) sprop; return JS_TRUE; } proto = LOCKED_OBJ_GET_PROTO(obj); JS_UNLOCK_OBJ(cx, obj); if (!proto) break; if (!OBJ_IS_NATIVE(proto)) return OBJ_LOOKUP_PROPERTY(cx, proto, id, objp, propp); obj = proto; } out: *objp = NULL; *propp = NULL; return JS_TRUE; } JS_FRIEND_API(JSBool) js_FindProperty(JSContext *cx, jsid id, JSObject **objp, JSObject **pobjp, JSProperty **propp) { JSRuntime *rt; JSObject *obj, *pobj, *lastobj; JSScopeProperty *sprop; JSProperty *prop; rt = cx->runtime; obj = cx->fp->scopeChain; do { /* Try the property cache and return immediately on cache hit. */ if (OBJ_IS_NATIVE(obj)) { JS_LOCK_OBJ(cx, obj); PROPERTY_CACHE_TEST(&rt->propertyCache, obj, id, sprop); if (sprop) { JS_ASSERT(OBJ_IS_NATIVE(obj)); *objp = obj; *pobjp = obj; *propp = (JSProperty *) sprop; return JS_TRUE; } JS_UNLOCK_OBJ(cx, obj); } /* If cache miss, take the slow path. */ if (!OBJ_LOOKUP_PROPERTY(cx, obj, id, &pobj, &prop)) return JS_FALSE; if (prop) { if (OBJ_IS_NATIVE(pobj)) { sprop = (JSScopeProperty *) prop; PROPERTY_CACHE_FILL(&rt->propertyCache, pobj, id, sprop); } *objp = obj; *pobjp = pobj; *propp = prop; return JS_TRUE; } lastobj = obj; } while ((obj = OBJ_GET_PARENT(cx, obj)) != NULL); *objp = lastobj; *pobjp = NULL; *propp = NULL; return JS_TRUE; } JSObject * js_FindIdentifierBase(JSContext *cx, jsid id) { JSObject *obj, *pobj; JSProperty *prop; /* * Look for id's property along the "with" statement chain and the * statically-linked scope chain. */ if (!js_FindProperty(cx, id, &obj, &pobj, &prop)) return NULL; if (prop) { OBJ_DROP_PROPERTY(cx, pobj, prop); return obj; } /* * Use the top-level scope from the scope chain, which won't end in the * same scope as cx->globalObject for cross-context function calls. */ JS_ASSERT(obj); /* * Property not found. Give a strict warning if binding an undeclared * top-level variable. */ if (JS_HAS_STRICT_OPTION(cx)) { JSString *str = JSVAL_TO_STRING(ID_TO_VALUE(id)); if (!JS_ReportErrorFlagsAndNumber(cx, JSREPORT_WARNING | JSREPORT_STRICT, js_GetErrorMessage, NULL, JSMSG_UNDECLARED_VAR, JS_GetStringBytes(str))) { return NULL; } } return obj; } JSBool js_NativeGet(JSContext *cx, JSObject *obj, JSObject *pobj, JSScopeProperty *sprop, jsval *vp) { JSScope *scope; uint32 slot; int32 sample; JSTempValueRooter tvr; JSBool ok; JS_ASSERT(OBJ_IS_NATIVE(pobj)); JS_ASSERT(JS_IS_OBJ_LOCKED(cx, pobj)); scope = OBJ_SCOPE(pobj); JS_ASSERT(scope->object == pobj); slot = sprop->slot; *vp = (slot != SPROP_INVALID_SLOT) ? LOCKED_OBJ_GET_SLOT(pobj, slot) : JSVAL_VOID; if (SPROP_HAS_STUB_GETTER(sprop)) return JS_TRUE; sample = cx->runtime->propertyRemovals; JS_UNLOCK_SCOPE(cx, scope); JS_PUSH_TEMP_ROOT_SPROP(cx, sprop, &tvr); ok = SPROP_GET(cx, sprop, obj, pobj, vp); JS_POP_TEMP_ROOT(cx, &tvr); if (!ok) return JS_FALSE; JS_LOCK_SCOPE(cx, scope); JS_ASSERT(scope->object == pobj); if (SLOT_IN_SCOPE(slot, scope) && (JS_LIKELY(cx->runtime->propertyRemovals == sample) || SCOPE_GET_PROPERTY(scope, sprop->id) == sprop)) { LOCKED_OBJ_SET_SLOT(pobj, slot, *vp); } return JS_TRUE; } JSBool js_NativeSet(JSContext *cx, JSObject *obj, JSScopeProperty *sprop, jsval *vp) { JSScope *scope; uint32 slot; jsval pval; int32 sample; JSTempValueRooter tvr; JSBool ok; JS_ASSERT(OBJ_IS_NATIVE(obj)); JS_ASSERT(JS_IS_OBJ_LOCKED(cx, obj)); scope = OBJ_SCOPE(obj); JS_ASSERT(scope->object == obj); slot = sprop->slot; if (slot != SPROP_INVALID_SLOT) { pval = LOCKED_OBJ_GET_SLOT(obj, slot); /* If sprop has a stub setter, keep scope locked and just store *vp. */ if (SPROP_HAS_STUB_SETTER(sprop)) goto set_slot; } else { /* * Allow API consumers to create shared properties with stub setters. * Such properties lack value storage, so setting them is like writing * to /dev/null. */ if (SPROP_HAS_STUB_SETTER(sprop)) return JS_TRUE; pval = JSVAL_VOID; } sample = cx->runtime->propertyRemovals; JS_UNLOCK_SCOPE(cx, scope); JS_PUSH_TEMP_ROOT_SPROP(cx, sprop, &tvr); ok = SPROP_SET(cx, sprop, obj, obj, vp); JS_POP_TEMP_ROOT(cx, &tvr); if (!ok) return JS_FALSE; JS_LOCK_SCOPE(cx, scope); JS_ASSERT(scope->object == obj); if (SLOT_IN_SCOPE(slot, scope) && (JS_LIKELY(cx->runtime->propertyRemovals == sample) || SCOPE_GET_PROPERTY(scope, sprop->id) == sprop)) { set_slot: GC_POKE(cx, pval); LOCKED_OBJ_SET_SLOT(obj, slot, *vp); } return JS_TRUE; } JSBool js_GetProperty(JSContext *cx, JSObject *obj, jsid id, jsval *vp) { JSObject *obj2; JSProperty *prop; JSScopeProperty *sprop; /* * Handle old bug that took empty string as zero index. Also convert * string indices to integers if appropriate. */ CHECK_FOR_STRING_INDEX(id); if (!js_LookupProperty(cx, obj, id, &obj2, &prop)) return JS_FALSE; if (!prop) { jsbytecode *pc; *vp = JSVAL_VOID; if (!OBJ_GET_CLASS(cx, obj)->getProperty(cx, obj, ID_TO_VALUE(id), vp)) return JS_FALSE; /* * Give a strict warning if foo.bar is evaluated by a script for an * object foo with no property named 'bar'. */ if (JSVAL_IS_VOID(*vp) && cx->fp && (pc = cx->fp->pc)) { JSOp op; uintN flags; JSString *str; op = *pc; if (op == JSOP_GETXPROP || op == JSOP_GETXELEM) { flags = JSREPORT_ERROR; } else { if (!JS_HAS_STRICT_OPTION(cx) || (op != JSOP_GETPROP && op != JSOP_GETELEM)) { return JS_TRUE; } /* * XXX do not warn about missing __iterator__ as the function * may be called from JS_GetMethodById. See bug 355145. */ if (id == ATOM_TO_JSID(cx->runtime->atomState.iteratorAtom)) return JS_TRUE; /* Kludge to allow (typeof foo == "undefined") tests. */ JS_ASSERT(cx->fp->script); pc += js_CodeSpec[op].length; if (Detecting(cx, pc)) return JS_TRUE; flags = JSREPORT_WARNING | JSREPORT_STRICT; } /* Ok, bad undefined property reference: whine about it. */ str = js_DecompileValueGenerator(cx, JSDVG_IGNORE_STACK, ID_TO_VALUE(id), NULL); if (!str || !JS_ReportErrorFlagsAndNumber(cx, flags, js_GetErrorMessage, NULL, JSMSG_UNDEFINED_PROP, JS_GetStringBytes(str))) { return JS_FALSE; } } return JS_TRUE; } if (!OBJ_IS_NATIVE(obj2)) { OBJ_DROP_PROPERTY(cx, obj2, prop); return OBJ_GET_PROPERTY(cx, obj2, id, vp); } sprop = (JSScopeProperty *) prop; if (!js_NativeGet(cx, obj, obj2, sprop, vp)) return JS_FALSE; PROPERTY_CACHE_FILL(&cx->runtime->propertyCache, obj2, id, sprop); JS_UNLOCK_OBJ(cx, obj2); return JS_TRUE; } JSBool js_SetProperty(JSContext *cx, JSObject *obj, jsid id, jsval *vp) { JSObject *pobj; JSProperty *prop; JSScopeProperty *sprop; JSScope *scope; uintN attrs, flags; intN shortid; JSClass *clasp; JSPropertyOp getter, setter; /* * Handle old bug that took empty string as zero index. Also convert * string indices to integers if appropriate. */ CHECK_FOR_STRING_INDEX(id); if (!js_LookupProperty(cx, obj, id, &pobj, &prop)) return JS_FALSE; if (prop && !OBJ_IS_NATIVE(pobj)) { OBJ_DROP_PROPERTY(cx, pobj, prop); prop = NULL; } sprop = (JSScopeProperty *) prop; /* * Now either sprop is null, meaning id was not found in obj or one of its * prototypes; or sprop is non-null, meaning id was found in pobj's scope. * If JS_THREADSAFE and sprop is non-null, then scope is locked, and sprop * is held: we must OBJ_DROP_PROPERTY or JS_UNLOCK_SCOPE before we return * (the two are equivalent for native objects, but we use JS_UNLOCK_SCOPE * because it is cheaper). */ attrs = JSPROP_ENUMERATE; flags = 0; shortid = 0; clasp = OBJ_GET_CLASS(cx, obj); getter = clasp->getProperty; setter = clasp->setProperty; if (sprop) { /* * Set scope for use below. It was locked by js_LookupProperty, and * we know pobj owns it (i.e., scope->object == pobj). Therefore we * optimize JS_UNLOCK_OBJ(cx, pobj) into JS_UNLOCK_SCOPE(cx, scope). */ scope = OBJ_SCOPE(pobj); attrs = sprop->attrs; if ((attrs & JSPROP_READONLY) || (SCOPE_IS_SEALED(scope) && pobj == obj)) { JS_UNLOCK_SCOPE(cx, scope); /* * Here, we'll either return true or goto read_only_error, which * reports a strict warning or throws an error. So we redefine * the |flags| local variable to be JSREPORT_* flags to pass to * JS_ReportErrorFlagsAndNumberUC at label read_only_error. We * must likewise re-task flags further below for the other 'goto * read_only_error;' case. */ flags = JSREPORT_ERROR; if ((attrs & JSPROP_READONLY) && JS_VERSION_IS_ECMA(cx)) { if (!JS_HAS_STRICT_OPTION(cx)) { /* Just return true per ECMA if not in strict mode. */ return JS_TRUE; } /* Strict mode: report a read-only strict warning. */ flags = JSREPORT_STRICT | JSREPORT_WARNING; } goto read_only_error; } if (pobj != obj) { /* * We found id in a prototype object: prepare to share or shadow. * NB: Thanks to the immutable, garbage-collected property tree * maintained by jsscope.c in cx->runtime, we needn't worry about * sprop going away behind our back after we've unlocked scope. */ JS_UNLOCK_SCOPE(cx, scope); /* Don't clone a shared prototype property. */ if (attrs & JSPROP_SHARED) { if (SPROP_HAS_STUB_SETTER(sprop) && !(sprop->attrs & JSPROP_GETTER)) { return JS_TRUE; } return SPROP_SET(cx, sprop, obj, pobj, vp); } /* Restore attrs to the ECMA default for new properties. */ attrs = JSPROP_ENUMERATE; /* * Preserve the shortid, getter, and setter when shadowing any * property that has a shortid. An old API convention requires * that the property's getter and setter functions receive the * shortid, not id, when they are called on the shadow we are * about to create in obj's scope. */ if (sprop->flags & SPROP_HAS_SHORTID) { flags = SPROP_HAS_SHORTID; shortid = sprop->shortid; getter = sprop->getter; setter = sprop->setter; } /* * Forget we found the proto-property now that we've copied any * needed member values. */ sprop = NULL; } #ifdef __GNUC__ /* suppress bogus gcc warnings */ } else { scope = NULL; #endif } if (!sprop) { if (SCOPE_IS_SEALED(OBJ_SCOPE(obj)) && OBJ_SCOPE(obj)->object == obj) { flags = JSREPORT_ERROR; goto read_only_error; } /* Find or make a property descriptor with the right heritage. */ JS_LOCK_OBJ(cx, obj); scope = js_GetMutableScope(cx, obj); if (!scope) { JS_UNLOCK_OBJ(cx, obj); return JS_FALSE; } if (clasp->flags & JSCLASS_SHARE_ALL_PROPERTIES) attrs |= JSPROP_SHARED; sprop = js_AddScopeProperty(cx, scope, id, getter, setter, SPROP_INVALID_SLOT, attrs, flags, shortid); if (!sprop) { JS_UNLOCK_SCOPE(cx, scope); return JS_FALSE; } /* * Initialize the new property value (passed to setter) to undefined. * Note that we store before calling addProperty, to match the order * in js_DefineNativeProperty. */ if (SPROP_HAS_VALID_SLOT(sprop, scope)) LOCKED_OBJ_SET_SLOT(obj, sprop->slot, JSVAL_VOID); /* XXXbe called with obj locked */ ADD_PROPERTY_HELPER(cx, clasp, obj, scope, sprop, vp, js_RemoveScopeProperty(cx, scope, id); JS_UNLOCK_SCOPE(cx, scope); return JS_FALSE); PROPERTY_CACHE_FILL(&cx->runtime->propertyCache, obj, id, sprop); } if (!js_NativeSet(cx, obj, sprop, vp)) return JS_FALSE; JS_UNLOCK_SCOPE(cx, scope); return JS_TRUE; read_only_error: { JSString *str = js_DecompileValueGenerator(cx, JSDVG_IGNORE_STACK, ID_TO_VALUE(id), NULL); if (!str) return JS_FALSE; return JS_ReportErrorFlagsAndNumberUC(cx, flags, js_GetErrorMessage, NULL, JSMSG_READ_ONLY, JS_GetStringChars(str)); } } JSBool js_GetAttributes(JSContext *cx, JSObject *obj, jsid id, JSProperty *prop, uintN *attrsp) { JSBool noprop, ok; JSScopeProperty *sprop; noprop = !prop; if (noprop) { if (!js_LookupProperty(cx, obj, id, &obj, &prop)) return JS_FALSE; if (!prop) { *attrsp = 0; return JS_TRUE; } if (!OBJ_IS_NATIVE(obj)) { ok = OBJ_GET_ATTRIBUTES(cx, obj, id, prop, attrsp); OBJ_DROP_PROPERTY(cx, obj, prop); return ok; } } sprop = (JSScopeProperty *)prop; *attrsp = sprop->attrs; if (noprop) OBJ_DROP_PROPERTY(cx, obj, prop); return JS_TRUE; } JSBool js_SetAttributes(JSContext *cx, JSObject *obj, jsid id, JSProperty *prop, uintN *attrsp) { JSBool noprop, ok; JSScopeProperty *sprop; noprop = !prop; if (noprop) { if (!js_LookupProperty(cx, obj, id, &obj, &prop)) return JS_FALSE; if (!prop) return JS_TRUE; if (!OBJ_IS_NATIVE(obj)) { ok = OBJ_SET_ATTRIBUTES(cx, obj, id, prop, attrsp); OBJ_DROP_PROPERTY(cx, obj, prop); return ok; } } sprop = (JSScopeProperty *)prop; sprop = js_ChangeNativePropertyAttrs(cx, obj, sprop, *attrsp, 0, sprop->getter, sprop->setter); if (noprop) OBJ_DROP_PROPERTY(cx, obj, prop); return (sprop != NULL); } JSBool js_DeleteProperty(JSContext *cx, JSObject *obj, jsid id, jsval *rval) { JSObject *proto; JSProperty *prop; JSScopeProperty *sprop; JSString *str; JSScope *scope; JSBool ok; *rval = JSVAL_TRUE; /* * Handle old bug that took empty string as zero index. Also convert * string indices to integers if appropriate. */ CHECK_FOR_STRING_INDEX(id); if (!js_LookupProperty(cx, obj, id, &proto, &prop)) return JS_FALSE; if (!prop || proto != obj) { /* * If the property was found in a native prototype, check whether it's * shared and permanent. Such a property stands for direct properties * in all delegating objects, matching ECMA semantics without bloating * each delegating object. */ if (prop) { if (OBJ_IS_NATIVE(proto)) { sprop = (JSScopeProperty *)prop; if (SPROP_IS_SHARED_PERMANENT(sprop)) *rval = JSVAL_FALSE; } OBJ_DROP_PROPERTY(cx, proto, prop); if (*rval == JSVAL_FALSE) return JS_TRUE; } /* * If no property, or the property comes unshared or impermanent from * a prototype, call the class's delProperty hook, passing rval as the * result parameter. */ return OBJ_GET_CLASS(cx, obj)->delProperty(cx, obj, ID_TO_VALUE(id), rval); } sprop = (JSScopeProperty *)prop; if (sprop->attrs & JSPROP_PERMANENT) { OBJ_DROP_PROPERTY(cx, obj, prop); if (JS_VERSION_IS_ECMA(cx)) { *rval = JSVAL_FALSE; return JS_TRUE; } str = js_DecompileValueGenerator(cx, JSDVG_IGNORE_STACK, ID_TO_VALUE(id), NULL); if (str) { JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_PERMANENT, JS_GetStringBytes(str)); } return JS_FALSE; } /* XXXbe called with obj locked */ if (!LOCKED_OBJ_GET_CLASS(obj)->delProperty(cx, obj, SPROP_USERID(sprop), rval)) { OBJ_DROP_PROPERTY(cx, obj, prop); return JS_FALSE; } scope = OBJ_SCOPE(obj); if (SPROP_HAS_VALID_SLOT(sprop, scope)) GC_POKE(cx, LOCKED_OBJ_GET_SLOT(obj, sprop->slot)); PROPERTY_CACHE_FILL(&cx->runtime->propertyCache, obj, id, NULL); ok = js_RemoveScopeProperty(cx, scope, id); OBJ_DROP_PROPERTY(cx, obj, prop); return ok; } JSBool js_DefaultValue(JSContext *cx, JSObject *obj, JSType hint, jsval *vp) { jsval v, save; JSString *str; v = save = OBJECT_TO_JSVAL(obj); switch (hint) { case JSTYPE_STRING: /* * Propagate the exception if js_TryMethod finds an appropriate * method, and calling that method returned failure. */ if (!js_TryMethod(cx, obj, cx->runtime->atomState.toStringAtom, 0, NULL, &v)) { return JS_FALSE; } if (!JSVAL_IS_PRIMITIVE(v)) { if (!OBJ_GET_CLASS(cx, obj)->convert(cx, obj, hint, &v)) return JS_FALSE; } break; default: if (!OBJ_GET_CLASS(cx, obj)->convert(cx, obj, hint, &v)) return JS_FALSE; if (!JSVAL_IS_PRIMITIVE(v)) { JSType type = JS_TypeOfValue(cx, v); if (type == hint || (type == JSTYPE_FUNCTION && hint == JSTYPE_OBJECT)) { goto out; } if (!js_TryMethod(cx, obj, cx->runtime->atomState.toStringAtom, 0, NULL, &v)) { return JS_FALSE; } } break; } if (!JSVAL_IS_PRIMITIVE(v)) { /* Avoid recursive death through js_DecompileValueGenerator. */ if (hint == JSTYPE_STRING) { str = JS_InternString(cx, OBJ_GET_CLASS(cx, obj)->name); if (!str) return JS_FALSE; } else { str = NULL; } *vp = OBJECT_TO_JSVAL(obj); str = js_DecompileValueGenerator(cx, JSDVG_SEARCH_STACK, save, str); if (str) { JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_CANT_CONVERT_TO, JS_GetStringBytes(str), (hint == JSTYPE_VOID) ? "primitive type" : js_type_strs[hint]); } return JS_FALSE; } out: *vp = v; return JS_TRUE; } JSIdArray * js_NewIdArray(JSContext *cx, jsint length) { JSIdArray *ida; ida = (JSIdArray *) JS_malloc(cx, sizeof(JSIdArray) + (length-1) * sizeof(jsval)); if (ida) ida->length = length; return ida; } JSIdArray * js_SetIdArrayLength(JSContext *cx, JSIdArray *ida, jsint length) { JSIdArray *rida; rida = (JSIdArray *) JS_realloc(cx, ida, sizeof(JSIdArray) + (length-1) * sizeof(jsval)); if (!rida) JS_DestroyIdArray(cx, ida); else rida->length = length; return rida; } /* Private type used to iterate over all properties of a native JS object */ struct JSNativeIteratorState { jsint next_index; /* index into jsid array */ JSIdArray *ida; /* all property ids in enumeration */ JSNativeIteratorState *next; /* double-linked list support */ JSNativeIteratorState **prevp; }; /* * This function is used to enumerate the properties of native JSObjects * and those host objects that do not define a JSNewEnumerateOp-style iterator * function. */ JSBool js_Enumerate(JSContext *cx, JSObject *obj, JSIterateOp enum_op, jsval *statep, jsid *idp) { JSRuntime *rt; JSObject *proto; JSClass *clasp; JSEnumerateOp enumerate; JSScopeProperty *sprop, *lastProp; jsint i, length; JSScope *scope; JSIdArray *ida; JSNativeIteratorState *state; rt = cx->runtime; clasp = OBJ_GET_CLASS(cx, obj); enumerate = clasp->enumerate; if (clasp->flags & JSCLASS_NEW_ENUMERATE) return ((JSNewEnumerateOp) enumerate)(cx, obj, enum_op, statep, idp); switch (enum_op) { case JSENUMERATE_INIT: if (!enumerate(cx, obj)) return JS_FALSE; length = 0; /* * The set of all property ids is pre-computed when the iterator * is initialized so as to avoid problems with properties being * deleted during the iteration. */ JS_LOCK_OBJ(cx, obj); scope = OBJ_SCOPE(obj); /* * If this object shares a scope with its prototype, don't enumerate * its properties. Otherwise they will be enumerated a second time * when the prototype object is enumerated. */ proto = OBJ_GET_PROTO(cx, obj); if (proto && scope == OBJ_SCOPE(proto)) { ida = js_NewIdArray(cx, 0); if (!ida) { JS_UNLOCK_OBJ(cx, obj); return JS_FALSE; } } else { /* Object has a private scope; Enumerate all props in scope. */ for (sprop = lastProp = SCOPE_LAST_PROP(scope); sprop; sprop = sprop->parent) { if (( #ifdef DUMP_CALL_TABLE (cx->options & JSOPTION_LOGCALL_TOSOURCE) || #endif (sprop->attrs & JSPROP_ENUMERATE)) && !(sprop->flags & SPROP_IS_ALIAS) && (!SCOPE_HAD_MIDDLE_DELETE(scope) || SCOPE_HAS_PROPERTY(scope, sprop))) { length++; } } ida = js_NewIdArray(cx, length); if (!ida) { JS_UNLOCK_OBJ(cx, obj); return JS_FALSE; } i = length; for (sprop = lastProp; sprop; sprop = sprop->parent) { if (( #ifdef DUMP_CALL_TABLE (cx->options & JSOPTION_LOGCALL_TOSOURCE) || #endif (sprop->attrs & JSPROP_ENUMERATE)) && !(sprop->flags & SPROP_IS_ALIAS) && (!SCOPE_HAD_MIDDLE_DELETE(scope) || SCOPE_HAS_PROPERTY(scope, sprop))) { JS_ASSERT(i > 0); ida->vector[--i] = sprop->id; } } } JS_UNLOCK_OBJ(cx, obj); state = (JSNativeIteratorState *) JS_malloc(cx, sizeof(JSNativeIteratorState)); if (!state) { JS_DestroyIdArray(cx, ida); return JS_FALSE; } state->ida = ida; state->next_index = 0; JS_LOCK_RUNTIME(rt); state->next = rt->nativeIteratorStates; if (state->next) state->next->prevp = &state->next; state->prevp = &rt->nativeIteratorStates; *state->prevp = state; JS_UNLOCK_RUNTIME(rt); *statep = PRIVATE_TO_JSVAL(state); if (idp) *idp = INT_TO_JSVAL(length); break; case JSENUMERATE_NEXT: state = (JSNativeIteratorState *) JSVAL_TO_PRIVATE(*statep); ida = state->ida; length = ida->length; if (state->next_index != length) { *idp = ida->vector[state->next_index++]; break; } /* FALL THROUGH */ case JSENUMERATE_DESTROY: state = (JSNativeIteratorState *) JSVAL_TO_PRIVATE(*statep); JS_LOCK_RUNTIME(rt); JS_ASSERT(rt->nativeIteratorStates); JS_ASSERT(*state->prevp == state); if (state->next) { JS_ASSERT(state->next->prevp == &state->next); state->next->prevp = state->prevp; } *state->prevp = state->next; JS_UNLOCK_RUNTIME(rt); JS_DestroyIdArray(cx, state->ida); JS_free(cx, state); *statep = JSVAL_NULL; break; } return JS_TRUE; } void js_MarkNativeIteratorStates(JSContext *cx) { JSNativeIteratorState *state; jsid *cursor, *end, id; state = cx->runtime->nativeIteratorStates; if (!state) return; do { JS_ASSERT(*state->prevp == state); cursor = state->ida->vector; end = cursor + state->ida->length; for (; cursor != end; ++cursor) { id = *cursor; MARK_ID(cx, id); } } while ((state = state->next) != NULL); } JSBool js_CheckAccess(JSContext *cx, JSObject *obj, jsid id, JSAccessMode mode, jsval *vp, uintN *attrsp) { JSBool writing; JSObject *pobj; JSProperty *prop; JSClass *clasp; JSScopeProperty *sprop; JSCheckAccessOp check; writing = (mode & JSACC_WRITE) != 0; switch (mode & JSACC_TYPEMASK) { case JSACC_PROTO: pobj = obj; if (!writing) *vp = OBJ_GET_SLOT(cx, obj, JSSLOT_PROTO); *attrsp = JSPROP_PERMANENT; break; case JSACC_PARENT: JS_ASSERT(!writing); pobj = obj; *vp = OBJ_GET_SLOT(cx, obj, JSSLOT_PARENT); *attrsp = JSPROP_READONLY | JSPROP_PERMANENT; break; default: if (!js_LookupProperty(cx, obj, id, &pobj, &prop)) return JS_FALSE; if (!prop) { if (!writing) *vp = JSVAL_VOID; *attrsp = 0; clasp = OBJ_GET_CLASS(cx, obj); return !clasp->checkAccess || clasp->checkAccess(cx, obj, ID_TO_VALUE(id), mode, vp); } if (!OBJ_IS_NATIVE(pobj)) { OBJ_DROP_PROPERTY(cx, pobj, prop); return OBJ_CHECK_ACCESS(cx, pobj, id, mode, vp, attrsp); } sprop = (JSScopeProperty *)prop; *attrsp = sprop->attrs; if (!writing) { *vp = (SPROP_HAS_VALID_SLOT(sprop, OBJ_SCOPE(pobj))) ? LOCKED_OBJ_GET_SLOT(pobj, sprop->slot) : JSVAL_VOID; } OBJ_DROP_PROPERTY(cx, pobj, prop); } /* * If obj's class has a stub (null) checkAccess hook, use the per-runtime * checkObjectAccess callback, if configured. * * We don't want to require all classes to supply a checkAccess hook; we * need that hook only for certain classes used when precompiling scripts * and functions ("brutal sharing"). But for general safety of built-in * magic properties such as __proto__ and __parent__, we route all access * checks, even for classes that stub out checkAccess, through the global * checkObjectAccess hook. This covers precompilation-based sharing and * (possibly unintended) runtime sharing across trust boundaries. */ clasp = OBJ_GET_CLASS(cx, pobj); check = clasp->checkAccess; if (!check) check = cx->runtime->checkObjectAccess; return !check || check(cx, pobj, ID_TO_VALUE(id), mode, vp); } #ifdef JS_THREADSAFE void js_DropProperty(JSContext *cx, JSObject *obj, JSProperty *prop) { JS_UNLOCK_OBJ(cx, obj); } #endif static void ReportIsNotFunction(JSContext *cx, jsval *vp, uintN flags) { /* * The decompiler may need to access the args of the function in * progress rather than the one we had hoped to call. * So we switch the cx->fp to the frame below us. We stick the * current frame in the dormantFrameChain to protect it from gc. */ JSStackFrame *fp = cx->fp; if (fp->down) { JS_ASSERT(!fp->dormantNext); fp->dormantNext = cx->dormantFrameChain; cx->dormantFrameChain = fp; cx->fp = fp->down; } js_ReportIsNotFunction(cx, vp, flags); if (fp->down) { JS_ASSERT(cx->dormantFrameChain == fp); cx->dormantFrameChain = fp->dormantNext; fp->dormantNext = NULL; cx->fp = fp; } } #ifdef NARCISSUS static JSBool GetCurrentExecutionContext(JSContext *cx, JSObject *obj, jsval *rval) { JSObject *tmp; jsval xcval; while ((tmp = OBJ_GET_PARENT(cx, obj)) != NULL) obj = tmp; if (!OBJ_GET_PROPERTY(cx, obj, ATOM_TO_JSID(cx->runtime->atomState .ExecutionContextAtom), &xcval)) { return JS_FALSE; } if (JSVAL_IS_PRIMITIVE(xcval)) { JS_ReportError(cx, "invalid ExecutionContext in global object"); return JS_FALSE; } if (!OBJ_GET_PROPERTY(cx, JSVAL_TO_OBJECT(xcval), ATOM_TO_JSID(cx->runtime->atomState.currentAtom), rval)) { return JS_FALSE; } return JS_TRUE; } #endif JSBool js_Call(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) { JSClass *clasp; clasp = OBJ_GET_CLASS(cx, JSVAL_TO_OBJECT(argv[-2])); if (!clasp->call) { #ifdef NARCISSUS JSObject *callee, *args; jsval fval, nargv[3]; JSBool ok; callee = JSVAL_TO_OBJECT(argv[-2]); if (!OBJ_GET_PROPERTY(cx, callee, ATOM_TO_JSID(cx->runtime->atomState.callAtom), &fval)) { return JS_FALSE; } if (VALUE_IS_FUNCTION(cx, fval)) { if (!GetCurrentExecutionContext(cx, obj, &nargv[2])) return JS_FALSE; args = js_GetArgsObject(cx, cx->fp); if (!args) return JS_FALSE; nargv[0] = OBJECT_TO_JSVAL(obj); nargv[1] = OBJECT_TO_JSVAL(args); return js_InternalCall(cx, callee, fval, 3, nargv, rval); } if (JSVAL_IS_OBJECT(fval) && JSVAL_TO_OBJECT(fval) != callee) { argv[-2] = fval; ok = js_Call(cx, obj, argc, argv, rval); argv[-2] = OBJECT_TO_JSVAL(callee); return ok; } #endif ReportIsNotFunction(cx, &argv[-2], cx->fp->flags & JSFRAME_ITERATOR); return JS_FALSE; } return clasp->call(cx, obj, argc, argv, rval); } JSBool js_Construct(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) { JSClass *clasp; clasp = OBJ_GET_CLASS(cx, JSVAL_TO_OBJECT(argv[-2])); if (!clasp->construct) { #ifdef NARCISSUS JSObject *callee, *args; jsval cval, nargv[2]; JSBool ok; callee = JSVAL_TO_OBJECT(argv[-2]); if (!OBJ_GET_PROPERTY(cx, callee, ATOM_TO_JSID(cx->runtime->atomState .constructAtom), &cval)) { return JS_FALSE; } if (VALUE_IS_FUNCTION(cx, cval)) { if (!GetCurrentExecutionContext(cx, obj, &nargv[1])) return JS_FALSE; args = js_GetArgsObject(cx, cx->fp); if (!args) return JS_FALSE; nargv[0] = OBJECT_TO_JSVAL(args); return js_InternalCall(cx, callee, cval, 2, nargv, rval); } if (JSVAL_IS_OBJECT(cval) && JSVAL_TO_OBJECT(cval) != callee) { argv[-2] = cval; ok = js_Call(cx, obj, argc, argv, rval); argv[-2] = OBJECT_TO_JSVAL(callee); return ok; } #endif ReportIsNotFunction(cx, &argv[-2], JSV2F_CONSTRUCT); return JS_FALSE; } return clasp->construct(cx, obj, argc, argv, rval); } JSBool js_HasInstance(JSContext *cx, JSObject *obj, jsval v, JSBool *bp) { JSClass *clasp; JSString *str; clasp = OBJ_GET_CLASS(cx, obj); if (clasp->hasInstance) return clasp->hasInstance(cx, obj, v, bp); #ifdef NARCISSUS { jsval fval, rval; if (!OBJ_GET_PROPERTY(cx, obj, ATOM_TO_JSID(cx->runtime->atomState .hasInstanceAtom), &fval)) { return JS_FALSE; } if (VALUE_IS_FUNCTION(cx, fval)) { return js_InternalCall(cx, obj, fval, 1, &v, &rval) && js_ValueToBoolean(cx, rval, bp); } } #endif str = js_DecompileValueGenerator(cx, JSDVG_SEARCH_STACK, OBJECT_TO_JSVAL(obj), NULL); if (str) { JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_BAD_INSTANCEOF_RHS, JS_GetStringBytes(str)); } return JS_FALSE; } JSBool js_IsDelegate(JSContext *cx, JSObject *obj, jsval v, JSBool *bp) { JSObject *obj2; *bp = JS_FALSE; if (JSVAL_IS_PRIMITIVE(v)) return JS_TRUE; obj2 = JSVAL_TO_OBJECT(v); while ((obj2 = OBJ_GET_PROTO(cx, obj2)) != NULL) { if (obj2 == obj) { *bp = JS_TRUE; break; } } return JS_TRUE; } JSBool js_GetClassPrototype(JSContext *cx, JSObject *scope, jsid id, JSObject **protop) { jsval v; JSObject *ctor; if (!js_FindClassObject(cx, scope, id, &v)) return JS_FALSE; if (VALUE_IS_FUNCTION(cx, v)) { ctor = JSVAL_TO_OBJECT(v); if (!OBJ_GET_PROPERTY(cx, ctor, ATOM_TO_JSID(cx->runtime->atomState .classPrototypeAtom), &v)) { return JS_FALSE; } if (!JSVAL_IS_PRIMITIVE(v)) { /* * Set the newborn root in case v is otherwise unreferenced. * It's ok to overwrite newborn roots here, since the getter * called just above could have. Unlike the common GC rooting * model, our callers do not have to protect protop thanks to * this newborn root, since they all immediately create a new * instance that delegates to this object, or just query the * prototype for its class. */ cx->weakRoots.newborn[GCX_OBJECT] = JSVAL_TO_GCTHING(v); } } *protop = JSVAL_IS_OBJECT(v) ? JSVAL_TO_OBJECT(v) : NULL; return JS_TRUE; } /* * For shared precompilation of function objects, we support cloning on entry * to an execution context in which the function declaration or expression * should be processed as if it were not precompiled, where the precompiled * function's scope chain does not match the execution context's. The cloned * function object carries its execution-context scope in its parent slot; it * links to the precompiled function (the "clone-parent") via its proto slot. * * Note that this prototype-based delegation leaves an unchecked access path * from the clone to the clone-parent's 'constructor' property. If the clone * lives in a less privileged or shared scope than the clone-parent, this is * a security hole, a sharing hazard, or both. Therefore we check all such * accesses with the following getter/setter pair, which we use when defining * 'constructor' in f.prototype for all function objects f. */ static JSBool CheckCtorGetAccess(JSContext *cx, JSObject *obj, jsval id, jsval *vp) { JSAtom *atom; uintN attrs; atom = cx->runtime->atomState.constructorAtom; JS_ASSERT(id == ATOM_KEY(atom)); return OBJ_CHECK_ACCESS(cx, obj, ATOM_TO_JSID(atom), JSACC_READ, vp, &attrs); } static JSBool CheckCtorSetAccess(JSContext *cx, JSObject *obj, jsval id, jsval *vp) { JSAtom *atom; uintN attrs; atom = cx->runtime->atomState.constructorAtom; JS_ASSERT(id == ATOM_KEY(atom)); return OBJ_CHECK_ACCESS(cx, obj, ATOM_TO_JSID(atom), JSACC_WRITE, vp, &attrs); } JSBool js_SetClassPrototype(JSContext *cx, JSObject *ctor, JSObject *proto, uintN attrs) { /* * Use the given attributes for the prototype property of the constructor, * as user-defined constructors have a DontDelete prototype (which may be * reset), while native or "system" constructors have DontEnum | ReadOnly | * DontDelete. */ if (!OBJ_DEFINE_PROPERTY(cx, ctor, ATOM_TO_JSID(cx->runtime->atomState .classPrototypeAtom), OBJECT_TO_JSVAL(proto), JS_PropertyStub, JS_PropertyStub, attrs, NULL)) { return JS_FALSE; } /* * ECMA says that Object.prototype.constructor, or f.prototype.constructor * for a user-defined function f, is DontEnum. */ return OBJ_DEFINE_PROPERTY(cx, proto, ATOM_TO_JSID(cx->runtime->atomState .constructorAtom), OBJECT_TO_JSVAL(ctor), CheckCtorGetAccess, CheckCtorSetAccess, 0, NULL); } JSBool js_ValueToObject(JSContext *cx, jsval v, JSObject **objp) { JSObject *obj; if (JSVAL_IS_NULL(v) || JSVAL_IS_VOID(v)) { obj = NULL; } else if (JSVAL_IS_OBJECT(v)) { obj = JSVAL_TO_OBJECT(v); if (!OBJ_DEFAULT_VALUE(cx, obj, JSTYPE_OBJECT, &v)) return JS_FALSE; if (JSVAL_IS_OBJECT(v)) obj = JSVAL_TO_OBJECT(v); } else { if (JSVAL_IS_STRING(v)) { obj = js_StringToObject(cx, JSVAL_TO_STRING(v)); } else if (JSVAL_IS_INT(v)) { obj = js_NumberToObject(cx, (jsdouble)JSVAL_TO_INT(v)); } else if (JSVAL_IS_DOUBLE(v)) { obj = js_NumberToObject(cx, *JSVAL_TO_DOUBLE(v)); } else { JS_ASSERT(JSVAL_IS_BOOLEAN(v)); obj = js_BooleanToObject(cx, JSVAL_TO_BOOLEAN(v)); } if (!obj) return JS_FALSE; } *objp = obj; return JS_TRUE; } JSObject * js_ValueToNonNullObject(JSContext *cx, jsval v) { JSObject *obj; JSString *str; if (!js_ValueToObject(cx, v, &obj)) return NULL; if (!obj) { str = js_DecompileValueGenerator(cx, JSDVG_SEARCH_STACK, v, NULL); if (str) { JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_NO_PROPERTIES, JS_GetStringBytes(str)); } } return obj; } JSBool js_TryValueOf(JSContext *cx, JSObject *obj, JSType type, jsval *rval) { jsval argv[1]; argv[0] = ATOM_KEY(cx->runtime->atomState.typeAtoms[type]); return js_TryMethod(cx, obj, cx->runtime->atomState.valueOfAtom, 1, argv, rval); } JSBool js_TryMethod(JSContext *cx, JSObject *obj, JSAtom *atom, uintN argc, jsval *argv, jsval *rval) { JSErrorReporter older; jsid id; jsval fval; JSBool ok; int stackDummy; if (!JS_CHECK_STACK_SIZE(cx, stackDummy)) { JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_OVER_RECURSED); return JS_FALSE; } /* * Report failure only if an appropriate method was found, and calling it * returned failure. We propagate failure in this case to make exceptions * behave properly. */ older = JS_SetErrorReporter(cx, NULL); id = ATOM_TO_JSID(atom); fval = JSVAL_VOID; #if JS_HAS_XML_SUPPORT if (OBJECT_IS_XML(cx, obj)) { JSXMLObjectOps *ops; ops = (JSXMLObjectOps *) obj->map->ops; obj = ops->getMethod(cx, obj, id, &fval); ok = (obj != NULL); } else #endif { ok = OBJ_GET_PROPERTY(cx, obj, id, &fval); } if (!ok) JS_ClearPendingException(cx); JS_SetErrorReporter(cx, older); return JSVAL_IS_PRIMITIVE(fval) || js_InternalCall(cx, obj, fval, argc, argv, rval); } #if JS_HAS_XDR JSBool js_XDRObject(JSXDRState *xdr, JSObject **objp) { JSContext *cx; JSAtom *atom; JSClass *clasp; uint32 classId, classDef; JSProtoKey protoKey; jsid classKey; JSObject *proto; cx = xdr->cx; atom = NULL; if (xdr->mode == JSXDR_ENCODE) { clasp = OBJ_GET_CLASS(cx, *objp); classId = JS_XDRFindClassIdByName(xdr, clasp->name); classDef = !classId; if (classDef) { if (!JS_XDRRegisterClass(xdr, clasp, &classId)) return JS_FALSE; protoKey = JSCLASS_CACHED_PROTO_KEY(clasp); if (protoKey != JSProto_Null) { classDef |= (protoKey << 1); } else { atom = js_Atomize(cx, clasp->name, strlen(clasp->name), 0); if (!atom) return JS_FALSE; } } } else { clasp = NULL; /* quell GCC overwarning */ classDef = 0; } /* * XDR a flag word, which could be 0 for a class use, in which case no * name follows, only the id in xdr's class registry; 1 for a class def, * in which case the flag word is followed by the class name transferred * from or to atom; or a value greater than 1, an odd number that when * divided by two yields the JSProtoKey for class. In the last case, as * in the 0 classDef case, no name is transferred via atom. */ if (!JS_XDRUint32(xdr, &classDef)) return JS_FALSE; if (classDef == 1 && !js_XDRCStringAtom(xdr, &atom)) return JS_FALSE; if (!JS_XDRUint32(xdr, &classId)) return JS_FALSE; if (xdr->mode == JSXDR_DECODE) { if (classDef) { /* NB: we know that JSProto_Null is 0 here, for backward compat. */ protoKey = classDef >> 1; classKey = (protoKey != JSProto_Null) ? INT_TO_JSID(protoKey) : ATOM_TO_JSID(atom); if (!js_GetClassPrototype(cx, NULL, classKey, &proto)) return JS_FALSE; clasp = OBJ_GET_CLASS(cx, proto); if (!JS_XDRRegisterClass(xdr, clasp, &classId)) return JS_FALSE; } else { clasp = JS_XDRFindClassById(xdr, classId); if (!clasp) { char numBuf[12]; JS_snprintf(numBuf, sizeof numBuf, "%ld", (long)classId); JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_CANT_FIND_CLASS, numBuf); return JS_FALSE; } } } if (!clasp->xdrObject) { JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_CANT_XDR_CLASS, clasp->name); return JS_FALSE; } return clasp->xdrObject(xdr, objp); } #endif /* JS_HAS_XDR */ #ifdef DEBUG_brendan #include #include uint32 js_entry_count_max; uint32 js_entry_count_sum; double js_entry_count_sqsum; uint32 js_entry_count_hist[11]; static void MeterEntryCount(uintN count) { if (count) { js_entry_count_sum += count; js_entry_count_sqsum += (double)count * count; if (count > js_entry_count_max) js_entry_count_max = count; } js_entry_count_hist[JS_MIN(count, 10)]++; } #define DEBUG_scopemeters #endif /* DEBUG_brendan */ #ifdef DEBUG_scopemeters void js_DumpScopeMeters(JSRuntime *rt) { static FILE *logfp; if (!logfp) logfp = fopen("/tmp/scope.stats", "a"); { double mean = 0., var = 0., sigma = 0.; double nscopes = rt->liveScopes; double nentrys = js_entry_count_sum; if (nscopes > 0 && nentrys >= 0) { mean = nentrys / nscopes; var = nscopes * js_entry_count_sqsum - nentrys * nentrys; if (var < 0.0 || nscopes <= 1) var = 0.0; else var /= nscopes * (nscopes - 1); /* Windows says sqrt(0.0) is "-1.#J" (?!) so we must test. */ sigma = (var != 0.) ? sqrt(var) : 0.; } fprintf(logfp, "scopes %g entries %g mean %g sigma %g max %u", nscopes, nentrys, mean, sigma, js_entry_count_max); } fprintf(logfp, " histogram %u %u %u %u %u %u %u %u %u %u %u\n", js_entry_count_hist[0], js_entry_count_hist[1], js_entry_count_hist[2], js_entry_count_hist[3], js_entry_count_hist[4], js_entry_count_hist[5], js_entry_count_hist[6], js_entry_count_hist[7], js_entry_count_hist[8], js_entry_count_hist[9], js_entry_count_hist[10]); js_entry_count_sum = js_entry_count_max = 0; js_entry_count_sqsum = 0; memset(js_entry_count_hist, 0, sizeof js_entry_count_hist); fflush(logfp); } #endif uint32 js_Mark(JSContext *cx, JSObject *obj, void *arg) { JSScope *scope; JSScopeProperty *sprop; JSClass *clasp; JS_ASSERT(OBJ_IS_NATIVE(obj)); scope = OBJ_SCOPE(obj); #ifdef DEBUG_brendan if (scope->object == obj) MeterEntryCount(scope->entryCount); #endif JS_ASSERT(!SCOPE_LAST_PROP(scope) || SCOPE_HAS_PROPERTY(scope, SCOPE_LAST_PROP(scope))); for (sprop = SCOPE_LAST_PROP(scope); sprop; sprop = sprop->parent) { if (SCOPE_HAD_MIDDLE_DELETE(scope) && !SCOPE_HAS_PROPERTY(scope, sprop)) continue; MARK_SCOPE_PROPERTY(cx, sprop); } /* No one runs while the GC is running, so we can use LOCKED_... here. */ clasp = LOCKED_OBJ_GET_CLASS(obj); if (clasp->mark) (void) clasp->mark(cx, obj, NULL); if (scope->object != obj) { /* * An unmutated object that shares a prototype's scope. We can't tell * how many slots are allocated and in use at obj->slots by looking at * scope, so we get obj->slots' length from its -1'st element. */ return (uint32) obj->slots[-1]; } return JS_MIN(scope->map.freeslot, scope->map.nslots); } void js_Clear(JSContext *cx, JSObject *obj) { JSScope *scope; JSRuntime *rt; JSScopeProperty *sprop; uint32 i, n; /* * Clear our scope and the property cache of all obj's properties only if * obj owns the scope (i.e., not if obj is unmutated and therefore sharing * its prototype's scope). NB: we do not clear any reserved slots lying * below JSSLOT_FREE(clasp). */ JS_LOCK_OBJ(cx, obj); scope = OBJ_SCOPE(obj); if (scope->object == obj) { /* Clear the property cache before we clear the scope. */ rt = cx->runtime; for (sprop = SCOPE_LAST_PROP(scope); sprop; sprop = sprop->parent) { if (!SCOPE_HAD_MIDDLE_DELETE(scope) || SCOPE_HAS_PROPERTY(scope, sprop)) { PROPERTY_CACHE_FILL(&rt->propertyCache, obj, sprop->id, NULL); } } /* Now that we're done using scope->lastProp/table, clear scope. */ js_ClearScope(cx, scope); /* Clear slot values and reset freeslot so we're consistent. */ i = scope->map.nslots; n = JSSLOT_FREE(LOCKED_OBJ_GET_CLASS(obj)); while (--i >= n) obj->slots[i] = JSVAL_VOID; scope->map.freeslot = n; } JS_UNLOCK_OBJ(cx, obj); } jsval js_GetRequiredSlot(JSContext *cx, JSObject *obj, uint32 slot) { jsval v; JS_LOCK_OBJ(cx, obj); v = (slot < (uint32) obj->slots[-1]) ? obj->slots[slot] : JSVAL_VOID; JS_UNLOCK_OBJ(cx, obj); return v; } JSBool js_SetRequiredSlot(JSContext *cx, JSObject *obj, uint32 slot, jsval v) { JSScope *scope; uint32 nslots; JSClass *clasp; jsval *newslots; JS_LOCK_OBJ(cx, obj); scope = OBJ_SCOPE(obj); nslots = (uint32) obj->slots[-1]; if (slot >= nslots) { /* * At this point, obj may or may not own scope. If some path calls * js_GetMutableScope but does not add a slot-owning property, then * scope->object == obj but nslots will be nominal. If obj shares a * prototype's scope, then we cannot update scope->map here, but we * must update obj->slots[-1] when we grow obj->slots. * * See js_Mark, before the last return, where we make a special case * for unmutated (scope->object != obj) objects. */ JS_ASSERT(nslots == JS_INITIAL_NSLOTS); clasp = LOCKED_OBJ_GET_CLASS(obj); nslots = JSSLOT_FREE(clasp); if (clasp->reserveSlots) nslots += clasp->reserveSlots(cx, obj); JS_ASSERT(slot < nslots); newslots = AllocSlots(cx, obj->slots, nslots); if (!newslots) { JS_UNLOCK_SCOPE(cx, scope); return JS_FALSE; } if (scope->object == obj) scope->map.nslots = nslots; obj->slots = newslots; } /* Whether or not we grew nslots, we may need to advance freeslot. */ if (scope->object == obj && slot >= scope->map.freeslot) scope->map.freeslot = slot + 1; obj->slots[slot] = v; JS_UNLOCK_SCOPE(cx, scope); return JS_TRUE; } #ifdef DEBUG /* Routines to print out values during debugging. */ void printChar(jschar *cp) { fprintf(stderr, "jschar* (0x%p) \"", (void *)cp); while (*cp) fputc(*cp++, stderr); fputc('"', stderr); fputc('\n', stderr); } void printString(JSString *str) { size_t i, n; jschar *s; fprintf(stderr, "string (0x%p) \"", (void *)str); s = JSSTRING_CHARS(str); for (i=0, n=JSSTRING_LENGTH(str); i < n; i++) fputc(s[i], stderr); fputc('"', stderr); fputc('\n', stderr); } void printVal(JSContext *cx, jsval val); void printObj(JSContext *cx, JSObject *jsobj) { jsuint i; jsval val; JSClass *clasp; fprintf(stderr, "object 0x%p\n", (void *)jsobj); clasp = OBJ_GET_CLASS(cx, jsobj); fprintf(stderr, "class 0x%p %s\n", (void *)clasp, clasp->name); for (i=0; i < jsobj->map->nslots; i++) { fprintf(stderr, "slot %3d ", i); val = jsobj->slots[i]; if (JSVAL_IS_OBJECT(val)) fprintf(stderr, "object 0x%p\n", (void *)JSVAL_TO_OBJECT(val)); else printVal(cx, val); } } void printVal(JSContext *cx, jsval val) { fprintf(stderr, "val %d (0x%p) = ", (int)val, (void *)val); if (JSVAL_IS_NULL(val)) { fprintf(stderr, "null\n"); } else if (JSVAL_IS_VOID(val)) { fprintf(stderr, "undefined\n"); } else if (JSVAL_IS_OBJECT(val)) { printObj(cx, JSVAL_TO_OBJECT(val)); } else if (JSVAL_IS_INT(val)) { fprintf(stderr, "(int) %d\n", JSVAL_TO_INT(val)); } else if (JSVAL_IS_STRING(val)) { printString(JSVAL_TO_STRING(val)); } else if (JSVAL_IS_DOUBLE(val)) { fprintf(stderr, "(double) %g\n", *JSVAL_TO_DOUBLE(val)); } else { JS_ASSERT(JSVAL_IS_BOOLEAN(val)); fprintf(stderr, "(boolean) %s\n", JSVAL_TO_BOOLEAN(val) ? "true" : "false"); } fflush(stderr); } void printId(JSContext *cx, jsid id) { fprintf(stderr, "id %d (0x%p) is ", (int)id, (void *)id); printVal(cx, ID_TO_VALUE(id)); } void printAtom(JSAtom *atom) { printString(ATOM_TO_STRING(atom)); } #endif pacparser-1.4.5/src/spidermonkey/js/src/jsobj.h000066400000000000000000000562231464010763600215030ustar00rootroot00000000000000/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- * vim: set ts=8 sw=4 et tw=80: * * ***** BEGIN LICENSE BLOCK ***** * Version: MPL 1.1/GPL 2.0/LGPL 2.1 * * The contents of this file are subject to the Mozilla Public License Version * 1.1 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * http://www.mozilla.org/MPL/ * * Software distributed under the License is distributed on an "AS IS" basis, * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License * for the specific language governing rights and limitations under the * License. * * The Original Code is Mozilla Communicator client code, released * March 31, 1998. * * The Initial Developer of the Original Code is * Netscape Communications Corporation. * Portions created by the Initial Developer are Copyright (C) 1998 * the Initial Developer. All Rights Reserved. * * Contributor(s): * * Alternatively, the contents of this file may be used under the terms of * either of the GNU General Public License Version 2 or later (the "GPL"), * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), * in which case the provisions of the GPL or the LGPL are applicable instead * of those above. If you wish to allow use of your version of this file only * under the terms of either the GPL or the LGPL, and not to allow others to * use your version of this file under the terms of the MPL, indicate your * decision by deleting the provisions above and replace them with the notice * and other provisions required by the GPL or the LGPL. If you do not delete * the provisions above, a recipient may use your version of this file under * the terms of any one of the MPL, the GPL or the LGPL. * * ***** END LICENSE BLOCK ***** */ #ifndef jsobj_h___ #define jsobj_h___ /* * JS object definitions. * * A JS object consists of a possibly-shared object descriptor containing * ordered property names, called the map; and a dense vector of property * values, called slots. The map/slot pointer pair is GC'ed, while the map * is reference counted and the slot vector is malloc'ed. */ #include "jshash.h" /* Added by JSIFY */ #include "jsprvtd.h" #include "jspubtd.h" JS_BEGIN_EXTERN_C struct JSObjectMap { jsrefcount nrefs; /* count of all referencing objects */ JSObjectOps *ops; /* high level object operation vtable */ uint32 nslots; /* length of obj->slots vector */ uint32 freeslot; /* index of next free obj->slots element */ }; /* Shorthand macros for frequently-made calls. */ #define OBJ_LOOKUP_PROPERTY(cx,obj,id,objp,propp) \ (obj)->map->ops->lookupProperty(cx,obj,id,objp,propp) #define OBJ_DEFINE_PROPERTY(cx,obj,id,value,getter,setter,attrs,propp) \ (obj)->map->ops->defineProperty(cx,obj,id,value,getter,setter,attrs,propp) #define OBJ_GET_PROPERTY(cx,obj,id,vp) \ (obj)->map->ops->getProperty(cx,obj,id,vp) #define OBJ_SET_PROPERTY(cx,obj,id,vp) \ (obj)->map->ops->setProperty(cx,obj,id,vp) #define OBJ_GET_ATTRIBUTES(cx,obj,id,prop,attrsp) \ (obj)->map->ops->getAttributes(cx,obj,id,prop,attrsp) #define OBJ_SET_ATTRIBUTES(cx,obj,id,prop,attrsp) \ (obj)->map->ops->setAttributes(cx,obj,id,prop,attrsp) #define OBJ_DELETE_PROPERTY(cx,obj,id,rval) \ (obj)->map->ops->deleteProperty(cx,obj,id,rval) #define OBJ_DEFAULT_VALUE(cx,obj,hint,vp) \ (obj)->map->ops->defaultValue(cx,obj,hint,vp) #define OBJ_ENUMERATE(cx,obj,enum_op,statep,idp) \ (obj)->map->ops->enumerate(cx,obj,enum_op,statep,idp) #define OBJ_CHECK_ACCESS(cx,obj,id,mode,vp,attrsp) \ (obj)->map->ops->checkAccess(cx,obj,id,mode,vp,attrsp) /* These four are time-optimized to avoid stub calls. */ #define OBJ_THIS_OBJECT(cx,obj) \ ((obj)->map->ops->thisObject \ ? (obj)->map->ops->thisObject(cx,obj) \ : (obj)) #define OBJ_DROP_PROPERTY(cx,obj,prop) \ ((obj)->map->ops->dropProperty \ ? (obj)->map->ops->dropProperty(cx,obj,prop) \ : (void)0) #define OBJ_GET_REQUIRED_SLOT(cx,obj,slot) \ ((obj)->map->ops->getRequiredSlot \ ? (obj)->map->ops->getRequiredSlot(cx, obj, slot) \ : JSVAL_VOID) #define OBJ_SET_REQUIRED_SLOT(cx,obj,slot,v) \ ((obj)->map->ops->setRequiredSlot \ ? (obj)->map->ops->setRequiredSlot(cx, obj, slot, v) \ : JS_TRUE) #define OBJ_TO_INNER_OBJECT(cx,obj) \ JS_BEGIN_MACRO \ JSClass *clasp_ = OBJ_GET_CLASS(cx, obj); \ if (clasp_->flags & JSCLASS_IS_EXTENDED) { \ JSExtendedClass *xclasp_ = (JSExtendedClass*)clasp_; \ if (xclasp_->innerObject) \ obj = xclasp_->innerObject(cx, obj); \ } \ JS_END_MACRO /* * In the original JS engine design, obj->slots pointed to a vector of length * JS_INITIAL_NSLOTS words if obj->map was shared with a prototype object, * else of length obj->map->nslots. With the advent of JS_GetReservedSlot, * JS_SetReservedSlot, and JSCLASS_HAS_RESERVED_SLOTS (see jsapi.h), the size * of the minimum length slots vector in the case where map is shared cannot * be constant. This length starts at JS_INITIAL_NSLOTS, but may advance to * include all the reserved slots. * * Therefore slots must be self-describing. Rather than tag its low order bit * (a bit is all we need) to distinguish initial length from reserved length, * we do "the BSTR thing": over-allocate slots by one jsval, and store the * *net* length (counting usable slots, which have non-negative obj->slots[] * indices) in obj->slots[-1]. All code that sets obj->slots must be aware of * this hack -- you have been warned, and jsobj.c has been updated! */ struct JSObject { JSObjectMap *map; jsval *slots; }; #define JSSLOT_PROTO 0 #define JSSLOT_PARENT 1 #define JSSLOT_CLASS 2 #define JSSLOT_PRIVATE 3 #define JSSLOT_START(clasp) (((clasp)->flags & JSCLASS_HAS_PRIVATE) \ ? JSSLOT_PRIVATE + 1 \ : JSSLOT_CLASS + 1) #define JSSLOT_FREE(clasp) (JSSLOT_START(clasp) \ + JSCLASS_RESERVED_SLOTS(clasp)) #define JS_INITIAL_NSLOTS 5 #ifdef DEBUG #define MAP_CHECK_SLOT(map,slot) \ JS_ASSERT((uint32)slot < JS_MIN((map)->freeslot, (map)->nslots)) #define OBJ_CHECK_SLOT(obj,slot) \ MAP_CHECK_SLOT((obj)->map, slot) #else #define OBJ_CHECK_SLOT(obj,slot) ((void)0) #endif /* Fast macros for accessing obj->slots while obj is locked (if thread-safe). */ #define LOCKED_OBJ_GET_SLOT(obj,slot) \ (OBJ_CHECK_SLOT(obj, slot), (obj)->slots[slot]) #define LOCKED_OBJ_SET_SLOT(obj,slot,value) \ (OBJ_CHECK_SLOT(obj, slot), (obj)->slots[slot] = (value)) #define LOCKED_OBJ_GET_PROTO(obj) \ JSVAL_TO_OBJECT(LOCKED_OBJ_GET_SLOT(obj, JSSLOT_PROTO)) #define LOCKED_OBJ_GET_CLASS(obj) \ ((JSClass *)JSVAL_TO_PRIVATE(LOCKED_OBJ_GET_SLOT(obj, JSSLOT_CLASS))) #ifdef JS_THREADSAFE /* Thread-safe functions and wrapper macros for accessing obj->slots. */ #define OBJ_GET_SLOT(cx,obj,slot) \ (OBJ_CHECK_SLOT(obj, slot), \ (OBJ_IS_NATIVE(obj) && OBJ_SCOPE(obj)->ownercx == cx) \ ? LOCKED_OBJ_GET_SLOT(obj, slot) \ : js_GetSlotThreadSafe(cx, obj, slot)) #define OBJ_SET_SLOT(cx,obj,slot,value) \ (OBJ_CHECK_SLOT(obj, slot), \ (OBJ_IS_NATIVE(obj) && OBJ_SCOPE(obj)->ownercx == cx) \ ? (void) LOCKED_OBJ_SET_SLOT(obj, slot, value) \ : js_SetSlotThreadSafe(cx, obj, slot, value)) /* * If thread-safe, define an OBJ_GET_SLOT wrapper that bypasses, for a native * object, the lock-free "fast path" test of (OBJ_SCOPE(obj)->ownercx == cx), * to avoid needlessly switching from lock-free to lock-full scope when doing * GC on a different context from the last one to own the scope. The caller * in this case is probably a JSClass.mark function, e.g., fun_mark, or maybe * a finalizer. * * The GC runs only when all threads except the one on which the GC is active * are suspended at GC-safe points, so there is no hazard in directly accessing * obj->slots[slot] from the GC's thread, once rt->gcRunning has been set. See * jsgc.c for details. */ #define THREAD_IS_RUNNING_GC(rt, thread) \ ((rt)->gcRunning && (rt)->gcThread == (thread)) #define CX_THREAD_IS_RUNNING_GC(cx) \ THREAD_IS_RUNNING_GC((cx)->runtime, (cx)->thread) #define GC_AWARE_GET_SLOT(cx, obj, slot) \ ((OBJ_IS_NATIVE(obj) && CX_THREAD_IS_RUNNING_GC(cx)) \ ? (obj)->slots[slot] \ : OBJ_GET_SLOT(cx, obj, slot)) #else /* !JS_THREADSAFE */ #define OBJ_GET_SLOT(cx,obj,slot) LOCKED_OBJ_GET_SLOT(obj,slot) #define OBJ_SET_SLOT(cx,obj,slot,value) LOCKED_OBJ_SET_SLOT(obj,slot,value) #define GC_AWARE_GET_SLOT(cx,obj,slot) LOCKED_OBJ_GET_SLOT(obj,slot) #endif /* !JS_THREADSAFE */ /* Thread-safe proto, parent, and class access macros. */ #define OBJ_GET_PROTO(cx,obj) \ JSVAL_TO_OBJECT(OBJ_GET_SLOT(cx, obj, JSSLOT_PROTO)) #define OBJ_SET_PROTO(cx,obj,proto) \ OBJ_SET_SLOT(cx, obj, JSSLOT_PROTO, OBJECT_TO_JSVAL(proto)) #define OBJ_GET_PARENT(cx,obj) \ JSVAL_TO_OBJECT(OBJ_GET_SLOT(cx, obj, JSSLOT_PARENT)) #define OBJ_SET_PARENT(cx,obj,parent) \ OBJ_SET_SLOT(cx, obj, JSSLOT_PARENT, OBJECT_TO_JSVAL(parent)) #define OBJ_GET_CLASS(cx,obj) \ ((JSClass *)JSVAL_TO_PRIVATE(OBJ_GET_SLOT(cx, obj, JSSLOT_CLASS))) /* Test whether a map or object is native. */ #define MAP_IS_NATIVE(map) \ ((map)->ops == &js_ObjectOps || \ ((map)->ops && (map)->ops->newObjectMap == js_ObjectOps.newObjectMap)) #define OBJ_IS_NATIVE(obj) MAP_IS_NATIVE((obj)->map) extern JS_FRIEND_DATA(JSObjectOps) js_ObjectOps; extern JS_FRIEND_DATA(JSObjectOps) js_WithObjectOps; extern JSClass js_ObjectClass; extern JSClass js_WithClass; extern JSClass js_BlockClass; /* * Block scope object macros. The slots reserved by js_BlockClass are: * * JSSLOT_PRIVATE JSStackFrame * active frame pointer or null * JSSLOT_BLOCK_DEPTH int depth of block slots in frame * * After JSSLOT_BLOCK_DEPTH come one or more slots for the block locals. * OBJ_BLOCK_COUNT depends on this arrangement. * * A With object is like a Block object, in that both have one reserved slot * telling the stack depth of the relevant slots (the slot whose value is the * object named in the with statement, the slots containing the block's local * variables); and both have a private slot referring to the JSStackFrame in * whose activation they were created (or null if the with or block object * outlives the frame). */ #define JSSLOT_BLOCK_DEPTH (JSSLOT_PRIVATE + 1) #define OBJ_BLOCK_COUNT(cx,obj) \ ((obj)->map->freeslot - (JSSLOT_BLOCK_DEPTH + 1)) #define OBJ_BLOCK_DEPTH(cx,obj) \ JSVAL_TO_INT(OBJ_GET_SLOT(cx, obj, JSSLOT_BLOCK_DEPTH)) #define OBJ_SET_BLOCK_DEPTH(cx,obj,depth) \ OBJ_SET_SLOT(cx, obj, JSSLOT_BLOCK_DEPTH, INT_TO_JSVAL(depth)) /* * To make sure this slot is well-defined, always call js_NewWithObject to * create a With object, don't call js_NewObject directly. When creating a * With object that does not correspond to a stack slot, pass -1 for depth. * * When popping the stack across this object's "with" statement, client code * must call JS_SetPrivate(cx, withobj, NULL). */ extern JSObject * js_NewWithObject(JSContext *cx, JSObject *proto, JSObject *parent, jsint depth); /* * Create a new block scope object not linked to any proto or parent object. * Blocks are created by the compiler to reify let blocks and comprehensions. * Only when dynamic scope is captured do they need to be cloned and spliced * into an active scope chain. */ extern JSObject * js_NewBlockObject(JSContext *cx); extern JSObject * js_CloneBlockObject(JSContext *cx, JSObject *proto, JSObject *parent, JSStackFrame *fp); extern JSBool js_PutBlockObject(JSContext *cx, JSObject *obj); struct JSSharpObjectMap { jsrefcount depth; jsatomid sharpgen; JSHashTable *table; }; #define SHARP_BIT ((jsatomid) 1) #define BUSY_BIT ((jsatomid) 2) #define SHARP_ID_SHIFT 2 #define IS_SHARP(he) (JS_PTR_TO_UINT32((he)->value) & SHARP_BIT) #define MAKE_SHARP(he) ((he)->value = JS_UINT32_TO_PTR(JS_PTR_TO_UINT32((he)->value)|SHARP_BIT)) #define IS_BUSY(he) (JS_PTR_TO_UINT32((he)->value) & BUSY_BIT) #define MAKE_BUSY(he) ((he)->value = JS_UINT32_TO_PTR(JS_PTR_TO_UINT32((he)->value)|BUSY_BIT)) #define CLEAR_BUSY(he) ((he)->value = JS_UINT32_TO_PTR(JS_PTR_TO_UINT32((he)->value)&~BUSY_BIT)) extern JSHashEntry * js_EnterSharpObject(JSContext *cx, JSObject *obj, JSIdArray **idap, jschar **sp); extern void js_LeaveSharpObject(JSContext *cx, JSIdArray **idap); /* * Mark objects stored in map if GC happens between js_EnterSharpObject * and js_LeaveSharpObject. GC calls this when map->depth > 0. */ extern void js_GCMarkSharpMap(JSContext *cx, JSSharpObjectMap *map); extern JSBool js_obj_toSource(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval); extern JSBool js_obj_toString(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval); extern JSBool js_HasOwnPropertyHelper(JSContext *cx, JSObject *obj, JSLookupPropOp lookup, uintN argc, jsval *argv, jsval *rval); extern JSObject* js_InitBlockClass(JSContext *cx, JSObject* obj); extern JSObject * js_InitObjectClass(JSContext *cx, JSObject *obj); /* Select Object.prototype method names shared between jsapi.c and jsobj.c. */ extern const char js_watch_str[]; extern const char js_unwatch_str[]; extern const char js_hasOwnProperty_str[]; extern const char js_isPrototypeOf_str[]; extern const char js_propertyIsEnumerable_str[]; extern const char js_defineGetter_str[]; extern const char js_defineSetter_str[]; extern const char js_lookupGetter_str[]; extern const char js_lookupSetter_str[]; extern void js_InitObjectMap(JSObjectMap *map, jsrefcount nrefs, JSObjectOps *ops, JSClass *clasp); extern JSObjectMap * js_NewObjectMap(JSContext *cx, jsrefcount nrefs, JSObjectOps *ops, JSClass *clasp, JSObject *obj); extern void js_DestroyObjectMap(JSContext *cx, JSObjectMap *map); extern JSObjectMap * js_HoldObjectMap(JSContext *cx, JSObjectMap *map); extern JSObjectMap * js_DropObjectMap(JSContext *cx, JSObjectMap *map, JSObject *obj); extern JSBool js_GetClassId(JSContext *cx, JSClass *clasp, jsid *idp); extern JSObject * js_NewObject(JSContext *cx, JSClass *clasp, JSObject *proto, JSObject *parent); /* * Fast access to immutable standard objects (constructors and prototypes). */ extern JSBool js_GetClassObject(JSContext *cx, JSObject *obj, JSProtoKey key, JSObject **objp); extern JSBool js_SetClassObject(JSContext *cx, JSObject *obj, JSProtoKey key, JSObject *cobj); extern JSBool js_FindClassObject(JSContext *cx, JSObject *start, jsid id, jsval *vp); extern JSObject * js_ConstructObject(JSContext *cx, JSClass *clasp, JSObject *proto, JSObject *parent, uintN argc, jsval *argv); extern void js_FinalizeObject(JSContext *cx, JSObject *obj); extern JSBool js_AllocSlot(JSContext *cx, JSObject *obj, uint32 *slotp); extern void js_FreeSlot(JSContext *cx, JSObject *obj, uint32 slot); /* * Native property add and lookup variants that hide id in the hidden atom * subspace, so as to avoid collisions between internal properties such as * formal arguments and local variables in function objects, and externally * set properties with the same ids. */ extern JSScopeProperty * js_AddHiddenProperty(JSContext *cx, JSObject *obj, jsid id, JSPropertyOp getter, JSPropertyOp setter, uint32 slot, uintN attrs, uintN flags, intN shortid); extern JSBool js_LookupHiddenProperty(JSContext *cx, JSObject *obj, jsid id, JSObject **objp, JSProperty **propp); /* * Find or create a property named by id in obj's scope, with the given getter * and setter, slot, attributes, and other members. */ extern JSScopeProperty * js_AddNativeProperty(JSContext *cx, JSObject *obj, jsid id, JSPropertyOp getter, JSPropertyOp setter, uint32 slot, uintN attrs, uintN flags, intN shortid); /* * Change sprop to have the given attrs, getter, and setter in scope, morphing * it into a potentially new JSScopeProperty. Return a pointer to the changed * or identical property. */ extern JSScopeProperty * js_ChangeNativePropertyAttrs(JSContext *cx, JSObject *obj, JSScopeProperty *sprop, uintN attrs, uintN mask, JSPropertyOp getter, JSPropertyOp setter); /* * On error, return false. On success, if propp is non-null, return true with * obj locked and with a held property in *propp; if propp is null, return true * but release obj's lock first. Therefore all callers who pass non-null propp * result parameters must later call OBJ_DROP_PROPERTY(cx, obj, *propp) both to * drop the held property, and to release the lock on obj. */ extern JSBool js_DefineProperty(JSContext *cx, JSObject *obj, jsid id, jsval value, JSPropertyOp getter, JSPropertyOp setter, uintN attrs, JSProperty **propp); extern JSBool js_DefineNativeProperty(JSContext *cx, JSObject *obj, jsid id, jsval value, JSPropertyOp getter, JSPropertyOp setter, uintN attrs, uintN flags, intN shortid, JSProperty **propp); /* * Unlike js_DefineProperty, propp must be non-null. On success, and if id was * found, return true with *objp non-null and locked, and with a held property * stored in *propp. If successful but id was not found, return true with both * *objp and *propp null. Therefore all callers who receive a non-null *propp * must later call OBJ_DROP_PROPERTY(cx, *objp, *propp). */ extern JS_FRIEND_API(JSBool) js_LookupProperty(JSContext *cx, JSObject *obj, jsid id, JSObject **objp, JSProperty **propp); /* * Specialized subroutine that allows caller to preset JSRESOLVE_* flags. * JSRESOLVE_HIDDEN flags hidden function param/local name lookups, just for * internal use by fun_resolve and similar built-ins. */ extern JSBool js_LookupPropertyWithFlags(JSContext *cx, JSObject *obj, jsid id, uintN flags, JSObject **objp, JSProperty **propp); #define JSRESOLVE_HIDDEN 0x8000 extern JS_FRIEND_API(JSBool) js_FindProperty(JSContext *cx, jsid id, JSObject **objp, JSObject **pobjp, JSProperty **propp); extern JSObject * js_FindIdentifierBase(JSContext *cx, jsid id); extern JSObject * js_FindVariableScope(JSContext *cx, JSFunction **funp); /* * NB: js_NativeGet and js_NativeSet are called with the scope containing sprop * (pobj's scope for Get, obj's for Set) locked, and on successful return, that * scope is again locked. But on failure, both functions return false with the * scope containing sprop unlocked. */ extern JSBool js_NativeGet(JSContext *cx, JSObject *obj, JSObject *pobj, JSScopeProperty *sprop, jsval *vp); extern JSBool js_NativeSet(JSContext *cx, JSObject *obj, JSScopeProperty *sprop, jsval *vp); extern JSBool js_GetProperty(JSContext *cx, JSObject *obj, jsid id, jsval *vp); extern JSBool js_SetProperty(JSContext *cx, JSObject *obj, jsid id, jsval *vp); extern JSBool js_GetAttributes(JSContext *cx, JSObject *obj, jsid id, JSProperty *prop, uintN *attrsp); extern JSBool js_SetAttributes(JSContext *cx, JSObject *obj, jsid id, JSProperty *prop, uintN *attrsp); extern JSBool js_DeleteProperty(JSContext *cx, JSObject *obj, jsid id, jsval *rval); extern JSBool js_DefaultValue(JSContext *cx, JSObject *obj, JSType hint, jsval *vp); extern JSIdArray * js_NewIdArray(JSContext *cx, jsint length); /* * Unlike realloc(3), this function frees ida on failure. */ extern JSIdArray * js_SetIdArrayLength(JSContext *cx, JSIdArray *ida, jsint length); extern JSBool js_Enumerate(JSContext *cx, JSObject *obj, JSIterateOp enum_op, jsval *statep, jsid *idp); extern void js_MarkNativeIteratorStates(JSContext *cx); extern JSBool js_CheckAccess(JSContext *cx, JSObject *obj, jsid id, JSAccessMode mode, jsval *vp, uintN *attrsp); extern JSBool js_Call(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval); extern JSBool js_Construct(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval); extern JSBool js_HasInstance(JSContext *cx, JSObject *obj, jsval v, JSBool *bp); extern JSBool js_SetProtoOrParent(JSContext *cx, JSObject *obj, uint32 slot, JSObject *pobj); extern JSBool js_IsDelegate(JSContext *cx, JSObject *obj, jsval v, JSBool *bp); extern JSBool js_GetClassPrototype(JSContext *cx, JSObject *scope, jsid id, JSObject **protop); extern JSBool js_SetClassPrototype(JSContext *cx, JSObject *ctor, JSObject *proto, uintN attrs); extern JSBool js_ValueToObject(JSContext *cx, jsval v, JSObject **objp); extern JSObject * js_ValueToNonNullObject(JSContext *cx, jsval v); extern JSBool js_TryValueOf(JSContext *cx, JSObject *obj, JSType type, jsval *rval); extern JSBool js_TryMethod(JSContext *cx, JSObject *obj, JSAtom *atom, uintN argc, jsval *argv, jsval *rval); extern JSBool js_XDRObject(JSXDRState *xdr, JSObject **objp); extern uint32 js_Mark(JSContext *cx, JSObject *obj, void *arg); extern void js_Clear(JSContext *cx, JSObject *obj); extern jsval js_GetRequiredSlot(JSContext *cx, JSObject *obj, uint32 slot); extern JSBool js_SetRequiredSlot(JSContext *cx, JSObject *obj, uint32 slot, jsval v); extern JSObject * js_CheckScopeChainValidity(JSContext *cx, JSObject *scopeobj, const char *caller); extern JSBool js_CheckPrincipalsAccess(JSContext *cx, JSObject *scopeobj, JSPrincipals *principals, JSAtom *caller); JS_END_EXTERN_C #endif /* jsobj_h___ */ pacparser-1.4.5/src/spidermonkey/js/src/jsopcode.c000066400000000000000000005121661464010763600222000ustar00rootroot00000000000000/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- * vim: set sw=4 ts=8 et tw=78: * * ***** BEGIN LICENSE BLOCK ***** * Version: MPL 1.1/GPL 2.0/LGPL 2.1 * * The contents of this file are subject to the Mozilla Public License Version * 1.1 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * http://www.mozilla.org/MPL/ * * Software distributed under the License is distributed on an "AS IS" basis, * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License * for the specific language governing rights and limitations under the * License. * * The Original Code is Mozilla Communicator client code, released * March 31, 1998. * * The Initial Developer of the Original Code is * Netscape Communications Corporation. * Portions created by the Initial Developer are Copyright (C) 1998 * the Initial Developer. All Rights Reserved. * * Contributor(s): * * Alternatively, the contents of this file may be used under the terms of * either of the GNU General Public License Version 2 or later (the "GPL"), * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), * in which case the provisions of the GPL or the LGPL are applicable instead * of those above. If you wish to allow use of your version of this file only * under the terms of either the GPL or the LGPL, and not to allow others to * use your version of this file under the terms of the MPL, indicate your * decision by deleting the provisions above and replace them with the notice * and other provisions required by the GPL or the LGPL. If you do not delete * the provisions above, a recipient may use your version of this file under * the terms of any one of the MPL, the GPL or the LGPL. * * ***** END LICENSE BLOCK ***** */ /* * JS bytecode descriptors, disassemblers, and decompilers. */ #include "jsstddef.h" #ifdef HAVE_MEMORY_H #include #endif #include #include #include #include #include "jstypes.h" #include "jsarena.h" /* Added by JSIFY */ #include "jsutil.h" /* Added by JSIFY */ #include "jsdtoa.h" #include "jsprf.h" #include "jsapi.h" #include "jsarray.h" #include "jsatom.h" #include "jscntxt.h" #include "jsconfig.h" #include "jsdbgapi.h" #include "jsemit.h" #include "jsfun.h" #include "jslock.h" #include "jsobj.h" #include "jsopcode.h" #include "jsregexp.h" #include "jsscan.h" #include "jsscope.h" #include "jsscript.h" #include "jsstr.h" #if JS_HAS_DESTRUCTURING # include "jsnum.h" #endif static const char js_incop_strs[][3] = {"++", "--"}; /* Pollute the namespace locally for MSVC Win16, but not for WatCom. */ #ifdef __WINDOWS_386__ #ifdef FAR #undef FAR #endif #else /* !__WINDOWS_386__ */ #ifndef FAR #define FAR #endif #endif /* !__WINDOWS_386__ */ const JSCodeSpec FAR js_CodeSpec[] = { #define OPDEF(op,val,name,token,length,nuses,ndefs,prec,format) \ {name,token,length,nuses,ndefs,prec,format}, #include "jsopcode.tbl" #undef OPDEF }; uintN js_NumCodeSpecs = sizeof (js_CodeSpec) / sizeof js_CodeSpec[0]; /************************************************************************/ static ptrdiff_t GetJumpOffset(jsbytecode *pc, jsbytecode *pc2) { uint32 type; type = (js_CodeSpec[*pc].format & JOF_TYPEMASK); if (JOF_TYPE_IS_EXTENDED_JUMP(type)) return GET_JUMPX_OFFSET(pc2); return GET_JUMP_OFFSET(pc2); } #ifdef DEBUG JS_FRIEND_API(JSBool) js_Disassemble(JSContext *cx, JSScript *script, JSBool lines, FILE *fp) { jsbytecode *pc, *end; uintN len; pc = script->code; end = pc + script->length; while (pc < end) { if (pc == script->main) fputs("main:\n", fp); len = js_Disassemble1(cx, script, pc, PTRDIFF(pc, script->code, jsbytecode), lines, fp); if (!len) return JS_FALSE; pc += len; } return JS_TRUE; } const char * ToDisassemblySource(JSContext *cx, jsval v) { JSObject *obj; JSScopeProperty *sprop; char *source; const char *bytes; JSString *str; if (!JSVAL_IS_PRIMITIVE(v)) { obj = JSVAL_TO_OBJECT(v); if (OBJ_GET_CLASS(cx, obj) == &js_BlockClass) { source = JS_sprintf_append(NULL, "depth %d {", OBJ_BLOCK_DEPTH(cx, obj)); for (sprop = OBJ_SCOPE(obj)->lastProp; sprop; sprop = sprop->parent) { bytes = js_AtomToPrintableString(cx, JSID_TO_ATOM(sprop->id)); if (!bytes) return NULL; source = JS_sprintf_append(source, "%s: %d%s", bytes, sprop->shortid, sprop->parent ? ", " : ""); } source = JS_sprintf_append(source, "}"); if (!source) return NULL; str = JS_NewString(cx, source, strlen(source)); if (!str) return NULL; return JS_GetStringBytes(str); } } return js_ValueToPrintableSource(cx, v); } JS_FRIEND_API(uintN) js_Disassemble1(JSContext *cx, JSScript *script, jsbytecode *pc, uintN loc, JSBool lines, FILE *fp) { JSOp op; const JSCodeSpec *cs; ptrdiff_t len, off, jmplen; uint32 type; JSAtom *atom; const char *bytes; op = (JSOp)*pc; if (op >= JSOP_LIMIT) { char numBuf1[12], numBuf2[12]; JS_snprintf(numBuf1, sizeof numBuf1, "%d", op); JS_snprintf(numBuf2, sizeof numBuf2, "%d", JSOP_LIMIT); JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_BYTECODE_TOO_BIG, numBuf1, numBuf2); return 0; } cs = &js_CodeSpec[op]; len = (ptrdiff_t) cs->length; fprintf(fp, "%05u:", loc); if (lines) fprintf(fp, "%4u", JS_PCToLineNumber(cx, script, pc)); fprintf(fp, " %s", cs->name); type = cs->format & JOF_TYPEMASK; switch (type) { case JOF_BYTE: if (op == JSOP_TRAP) { op = JS_GetTrapOpcode(cx, script, pc); if (op == JSOP_LIMIT) return 0; len = (ptrdiff_t) js_CodeSpec[op].length; } break; case JOF_JUMP: case JOF_JUMPX: off = GetJumpOffset(pc, pc); fprintf(fp, " %u (%d)", loc + off, off); break; case JOF_CONST: atom = GET_ATOM(cx, script, pc); bytes = ToDisassemblySource(cx, ATOM_KEY(atom)); if (!bytes) return 0; fprintf(fp, " %s", bytes); break; case JOF_UINT16: case JOF_LOCAL: fprintf(fp, " %u", GET_UINT16(pc)); break; case JOF_TABLESWITCH: case JOF_TABLESWITCHX: { jsbytecode *pc2; jsint i, low, high; jmplen = (type == JOF_TABLESWITCH) ? JUMP_OFFSET_LEN : JUMPX_OFFSET_LEN; pc2 = pc; off = GetJumpOffset(pc, pc2); pc2 += jmplen; low = GET_JUMP_OFFSET(pc2); pc2 += JUMP_OFFSET_LEN; high = GET_JUMP_OFFSET(pc2); pc2 += JUMP_OFFSET_LEN; fprintf(fp, " defaultOffset %d low %d high %d", off, low, high); for (i = low; i <= high; i++) { off = GetJumpOffset(pc, pc2); fprintf(fp, "\n\t%d: %d", i, off); pc2 += jmplen; } len = 1 + pc2 - pc; break; } case JOF_LOOKUPSWITCH: case JOF_LOOKUPSWITCHX: { jsbytecode *pc2; jsatomid npairs; jmplen = (type == JOF_LOOKUPSWITCH) ? JUMP_OFFSET_LEN : JUMPX_OFFSET_LEN; pc2 = pc; off = GetJumpOffset(pc, pc2); pc2 += jmplen; npairs = GET_ATOM_INDEX(pc2); pc2 += ATOM_INDEX_LEN; fprintf(fp, " offset %d npairs %u", off, (uintN) npairs); while (npairs) { atom = GET_ATOM(cx, script, pc2); pc2 += ATOM_INDEX_LEN; off = GetJumpOffset(pc, pc2); pc2 += jmplen; bytes = ToDisassemblySource(cx, ATOM_KEY(atom)); if (!bytes) return 0; fprintf(fp, "\n\t%s: %d", bytes, off); npairs--; } len = 1 + pc2 - pc; break; } case JOF_QARG: fprintf(fp, " %u", GET_ARGNO(pc)); break; case JOF_QVAR: fprintf(fp, " %u", GET_VARNO(pc)); break; case JOF_INDEXCONST: fprintf(fp, " %u", GET_VARNO(pc)); pc += VARNO_LEN; atom = GET_ATOM(cx, script, pc); bytes = ToDisassemblySource(cx, ATOM_KEY(atom)); if (!bytes) return 0; fprintf(fp, " %s", bytes); break; case JOF_UINT24: if (op == JSOP_FINDNAME) { /* Special case to avoid a JOF_FINDNAME just for this op. */ atom = js_GetAtom(cx, &script->atomMap, GET_UINT24(pc)); bytes = ToDisassemblySource(cx, ATOM_KEY(atom)); if (!bytes) return 0; fprintf(fp, " %s", bytes); break; } JS_ASSERT(op == JSOP_UINT24 || op == JSOP_LITERAL); fprintf(fp, " %u", GET_UINT24(pc)); break; case JOF_LITOPX: atom = js_GetAtom(cx, &script->atomMap, GET_LITERAL_INDEX(pc)); bytes = ToDisassemblySource(cx, ATOM_KEY(atom)); if (!bytes) return 0; /* * Bytecode: JSOP_LITOPX op [ if JSOP_DEFLOCALFUN]. * Advance pc to point at op. */ pc += 1 + LITERAL_INDEX_LEN; op = *pc; cs = &js_CodeSpec[op]; fprintf(fp, " %s op %s", bytes, cs->name); if ((cs->format & JOF_TYPEMASK) == JOF_INDEXCONST) fprintf(fp, " %u", GET_VARNO(pc)); /* * Set len to advance pc to skip op and any other immediates (namely, * if JSOP_DEFLOCALFUN). */ JS_ASSERT(cs->length > ATOM_INDEX_LEN); len = cs->length - ATOM_INDEX_LEN; break; default: { char numBuf[12]; JS_snprintf(numBuf, sizeof numBuf, "%lx", (unsigned long) cs->format); JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_UNKNOWN_FORMAT, numBuf); return 0; } } fputs("\n", fp); return len; } #endif /* DEBUG */ /************************************************************************/ /* * Sprintf, but with unlimited and automatically allocated buffering. */ typedef struct Sprinter { JSContext *context; /* context executing the decompiler */ JSArenaPool *pool; /* string allocation pool */ char *base; /* base address of buffer in pool */ size_t size; /* size of buffer allocated at base */ ptrdiff_t offset; /* offset of next free char in buffer */ } Sprinter; #define INIT_SPRINTER(cx, sp, ap, off) \ ((sp)->context = cx, (sp)->pool = ap, (sp)->base = NULL, (sp)->size = 0, \ (sp)->offset = off) #define OFF2STR(sp,off) ((sp)->base + (off)) #define STR2OFF(sp,str) ((str) - (sp)->base) #define RETRACT(sp,str) ((sp)->offset = STR2OFF(sp, str)) static JSBool SprintAlloc(Sprinter *sp, size_t nb) { char *base; base = sp->base; if (!base) { JS_ARENA_ALLOCATE_CAST(base, char *, sp->pool, nb); } else { JS_ARENA_GROW_CAST(base, char *, sp->pool, sp->size, nb); } if (!base) { JS_ReportOutOfMemory(sp->context); return JS_FALSE; } sp->base = base; sp->size += nb; return JS_TRUE; } static ptrdiff_t SprintPut(Sprinter *sp, const char *s, size_t len) { ptrdiff_t nb, offset; char *bp; /* Allocate space for s, including the '\0' at the end. */ nb = (sp->offset + len + 1) - sp->size; if (nb > 0 && !SprintAlloc(sp, nb)) return -1; /* Advance offset and copy s into sp's buffer. */ offset = sp->offset; sp->offset += len; bp = sp->base + offset; memmove(bp, s, len); bp[len] = 0; return offset; } static ptrdiff_t SprintCString(Sprinter *sp, const char *s) { return SprintPut(sp, s, strlen(s)); } static ptrdiff_t Sprint(Sprinter *sp, const char *format, ...) { va_list ap; char *bp; ptrdiff_t offset; va_start(ap, format); bp = JS_vsmprintf(format, ap); /* XXX vsaprintf */ va_end(ap); if (!bp) { JS_ReportOutOfMemory(sp->context); return -1; } offset = SprintCString(sp, bp); free(bp); return offset; } const jschar js_EscapeMap[] = { '\b', 'b', '\f', 'f', '\n', 'n', '\r', 'r', '\t', 't', '\v', 'v', '"', '"', '\'', '\'', '\\', '\\', 0 }; #define DONT_ESCAPE 0x10000 static char * QuoteString(Sprinter *sp, JSString *str, uint32 quote) { JSBool dontEscape, ok; jschar qc, c; ptrdiff_t off, len, nb; const jschar *s, *t, *u, *z; char *bp; /* Sample off first for later return value pointer computation. */ dontEscape = (quote & DONT_ESCAPE) != 0; qc = (jschar) quote; off = sp->offset; if (qc && Sprint(sp, "%c", (char)qc) < 0) return NULL; /* Loop control variables: z points at end of string sentinel. */ s = JSSTRING_CHARS(str); z = s + JSSTRING_LENGTH(str); for (t = s; t < z; s = ++t) { /* Move t forward from s past un-quote-worthy characters. */ c = *t; while (JS_ISPRINT(c) && c != qc && c != '\\' && !(c >> 8)) { c = *++t; if (t == z) break; } len = PTRDIFF(t, s, jschar); /* Allocate space for s, including the '\0' at the end. */ nb = (sp->offset + len + 1) - sp->size; if (nb > 0 && !SprintAlloc(sp, nb)) return NULL; /* Advance sp->offset and copy s into sp's buffer. */ bp = sp->base + sp->offset; sp->offset += len; while (--len >= 0) *bp++ = (char) *s++; *bp = '\0'; if (t == z) break; /* Use js_EscapeMap, \u, or \x only if necessary. */ if ((u = js_strchr(js_EscapeMap, c)) != NULL) { ok = dontEscape ? Sprint(sp, "%c", (char)c) >= 0 : Sprint(sp, "\\%c", (char)u[1]) >= 0; } else { #ifdef JS_C_STRINGS_ARE_UTF8 /* If this is a surrogate pair, make sure to print the pair. */ if (c >= 0xD800 && c <= 0xDBFF) { jschar buffer[3]; buffer[0] = c; buffer[1] = *++t; buffer[2] = 0; if (t == z) { char numbuf[10]; JS_snprintf(numbuf, sizeof numbuf, "0x%x", c); JS_ReportErrorFlagsAndNumber(sp->context, JSREPORT_ERROR, js_GetErrorMessage, NULL, JSMSG_BAD_SURROGATE_CHAR, numbuf); ok = JS_FALSE; break; } ok = Sprint(sp, "%hs", buffer) >= 0; } else { /* Print as UTF-8 string. */ ok = Sprint(sp, "%hc", c) >= 0; } #else /* Use \uXXXX or \xXX if the string can't be displayed as UTF-8. */ ok = Sprint(sp, (c >> 8) ? "\\u%04X" : "\\x%02X", c) >= 0; #endif } if (!ok) return NULL; } /* Sprint the closing quote and return the quoted string. */ if (qc && Sprint(sp, "%c", (char)qc) < 0) return NULL; /* * If we haven't Sprint'd anything yet, Sprint an empty string so that * the OFF2STR below gives a valid result. */ if (off == sp->offset && Sprint(sp, "") < 0) return NULL; return OFF2STR(sp, off); } JSString * js_QuoteString(JSContext *cx, JSString *str, jschar quote) { void *mark; Sprinter sprinter; char *bytes; JSString *escstr; mark = JS_ARENA_MARK(&cx->tempPool); INIT_SPRINTER(cx, &sprinter, &cx->tempPool, 0); bytes = QuoteString(&sprinter, str, quote); escstr = bytes ? JS_NewStringCopyZ(cx, bytes) : NULL; JS_ARENA_RELEASE(&cx->tempPool, mark); return escstr; } /************************************************************************/ #if JS_HAS_BLOCK_SCOPE typedef enum JSBraceState { ALWAYS_BRACE, MAYBE_BRACE, DONT_BRACE } JSBraceState; #endif struct JSPrinter { Sprinter sprinter; /* base class state */ JSArenaPool pool; /* string allocation pool */ uintN indent; /* indentation in spaces */ JSPackedBool pretty; /* pretty-print: indent, use newlines */ JSPackedBool grouped; /* in parenthesized expression context */ JSScript *script; /* script being printed */ jsbytecode *dvgfence; /* js_DecompileValueGenerator fencepost */ JSScope *scope; /* script function scope */ #if JS_HAS_BLOCK_SCOPE JSBraceState braceState; /* remove braces around let declaration */ ptrdiff_t spaceOffset; /* -1 or offset of space before maybe-{ */ #endif }; /* * Hack another flag, a la JS_DONT_PRETTY_PRINT, into uintN indent parameters * to functions such as js_DecompileFunction and js_NewPrinter. This time, as * opposed to JS_DONT_PRETTY_PRINT back in the dark ages, we can assume that a * uintN is at least 32 bits. */ #define JS_IN_GROUP_CONTEXT 0x10000 JSPrinter * js_NewPrinter(JSContext *cx, const char *name, uintN indent, JSBool pretty) { JSPrinter *jp; jp = (JSPrinter *) JS_malloc(cx, sizeof(JSPrinter)); if (!jp) return NULL; INIT_SPRINTER(cx, &jp->sprinter, &jp->pool, 0); JS_InitArenaPool(&jp->pool, name, 256, 1); jp->indent = indent & ~JS_IN_GROUP_CONTEXT; jp->pretty = pretty; jp->grouped = (indent & JS_IN_GROUP_CONTEXT) != 0; jp->script = NULL; jp->dvgfence = NULL; jp->scope = NULL; #if JS_HAS_BLOCK_SCOPE jp->braceState = ALWAYS_BRACE; jp->spaceOffset = -1; #endif return jp; } void js_DestroyPrinter(JSPrinter *jp) { JS_FinishArenaPool(&jp->pool); JS_free(jp->sprinter.context, jp); } JSString * js_GetPrinterOutput(JSPrinter *jp) { JSContext *cx; JSString *str; cx = jp->sprinter.context; if (!jp->sprinter.base) return cx->runtime->emptyString; str = JS_NewStringCopyZ(cx, jp->sprinter.base); if (!str) return NULL; JS_FreeArenaPool(&jp->pool); INIT_SPRINTER(cx, &jp->sprinter, &jp->pool, 0); return str; } #if !JS_HAS_BLOCK_SCOPE # define SET_MAYBE_BRACE(jp) jp # define CLEAR_MAYBE_BRACE(jp) jp #else # define SET_MAYBE_BRACE(jp) ((jp)->braceState = MAYBE_BRACE, (jp)) # define CLEAR_MAYBE_BRACE(jp) ((jp)->braceState = ALWAYS_BRACE, (jp)) static void SetDontBrace(JSPrinter *jp) { ptrdiff_t offset; const char *bp; /* When not pretty-printing, newline after brace is chopped. */ JS_ASSERT(jp->spaceOffset < 0); offset = jp->sprinter.offset - (jp->pretty ? 3 : 2); /* The shortest case is "if (x) {". */ JS_ASSERT(offset >= 6); bp = jp->sprinter.base; if (bp[offset+0] == ' ' && bp[offset+1] == '{') { JS_ASSERT(!jp->pretty || bp[offset+2] == '\n'); jp->spaceOffset = offset; jp->braceState = DONT_BRACE; } } #endif int js_printf(JSPrinter *jp, const char *format, ...) { va_list ap; char *bp, *fp; int cc; if (*format == '\0') return 0; va_start(ap, format); /* If pretty-printing, expand magic tab into a run of jp->indent spaces. */ if (*format == '\t') { format++; #if JS_HAS_BLOCK_SCOPE if (*format == '}' && jp->braceState != ALWAYS_BRACE) { JSBraceState braceState; braceState = jp->braceState; jp->braceState = ALWAYS_BRACE; if (braceState == DONT_BRACE) { ptrdiff_t offset, delta, from; JS_ASSERT(format[1] == '\n' || format[1] == ' '); offset = jp->spaceOffset; JS_ASSERT(offset >= 6); /* Replace " {\n" at the end of jp->sprinter with "\n". */ bp = jp->sprinter.base; if (bp[offset+0] == ' ' && bp[offset+1] == '{') { delta = 2; if (jp->pretty) { /* If pretty, we don't have to worry about 'else'. */ JS_ASSERT(bp[offset+2] == '\n'); } else if (bp[offset-1] != ')') { /* Must keep ' ' to avoid 'dolet' or 'elselet'. */ ++offset; delta = 1; } from = offset + delta; memmove(bp + offset, bp + from, jp->sprinter.offset - from); jp->sprinter.offset -= delta; jp->spaceOffset = -1; format += 2; if (*format == '\0') return 0; } } } #endif if (jp->pretty && Sprint(&jp->sprinter, "%*s", jp->indent, "") < 0) return -1; } /* Suppress newlines (must be once per format, at the end) if not pretty. */ fp = NULL; if (!jp->pretty && format[cc = strlen(format) - 1] == '\n') { fp = JS_strdup(jp->sprinter.context, format); if (!fp) return -1; fp[cc] = '\0'; format = fp; } /* Allocate temp space, convert format, and put. */ bp = JS_vsmprintf(format, ap); /* XXX vsaprintf */ if (fp) { JS_free(jp->sprinter.context, fp); format = NULL; } if (!bp) { JS_ReportOutOfMemory(jp->sprinter.context); return -1; } cc = strlen(bp); if (SprintPut(&jp->sprinter, bp, (size_t)cc) < 0) cc = -1; free(bp); va_end(ap); return cc; } JSBool js_puts(JSPrinter *jp, const char *s) { return SprintCString(&jp->sprinter, s) >= 0; } /************************************************************************/ typedef struct SprintStack { Sprinter sprinter; /* sprinter for postfix to infix buffering */ ptrdiff_t *offsets; /* stack of postfix string offsets */ jsbytecode *opcodes; /* parallel stack of JS opcodes */ uintN top; /* top of stack index */ uintN inArrayInit; /* array initialiser/comprehension level */ JSPrinter *printer; /* permanent output goes here */ } SprintStack; /* * Get a stacked offset from ss->sprinter.base, or if the stacked value |off| * is negative, lazily fetch the generating pc at |spindex = 1 + off| and try * to decompile the code that generated the missing value. This is used when * reporting errors, where the model stack will lack |pcdepth| non-negative * offsets (see js_DecompileValueGenerator and js_DecompileCode). * * If the stacked offset is -1, return 0 to index the NUL padding at the start * of ss->sprinter.base. If this happens, it means there is a decompiler bug * to fix, but it won't violate memory safety. */ static ptrdiff_t GetOff(SprintStack *ss, uintN i) { ptrdiff_t off; JSString *str; off = ss->offsets[i]; if (off < 0) { #if defined DEBUG_brendan || defined DEBUG_mrbkap || defined DEBUG_crowder JS_ASSERT(off < -1); #endif if (++off == 0) { if (!ss->sprinter.base && SprintPut(&ss->sprinter, "", 0) >= 0) memset(ss->sprinter.base, 0, ss->sprinter.offset); return 0; } str = js_DecompileValueGenerator(ss->sprinter.context, off, JSVAL_NULL, NULL); if (!str) return 0; off = SprintCString(&ss->sprinter, JS_GetStringBytes(str)); if (off < 0) off = 0; ss->offsets[i] = off; } return off; } static const char * GetStr(SprintStack *ss, uintN i) { ptrdiff_t off; /* * Must call GetOff before using ss->sprinter.base, since it may be null * until bootstrapped by GetOff. */ off = GetOff(ss, i); return OFF2STR(&ss->sprinter, off); } /* Gap between stacked strings to allow for insertion of parens and commas. */ #define PAREN_SLOP (2 + 1) /* * These pseudo-ops help js_DecompileValueGenerator decompile JSOP_SETNAME, * JSOP_SETPROP, and JSOP_SETELEM, respectively. They are never stored in * bytecode, so they don't preempt valid opcodes. */ #define JSOP_GETPROP2 256 #define JSOP_GETELEM2 257 static JSBool PushOff(SprintStack *ss, ptrdiff_t off, JSOp op) { uintN top; if (!SprintAlloc(&ss->sprinter, PAREN_SLOP)) return JS_FALSE; /* ss->top points to the next free slot; be paranoid about overflow. */ top = ss->top; JS_ASSERT(top < ss->printer->script->depth); if (top >= ss->printer->script->depth) { JS_ReportOutOfMemory(ss->sprinter.context); return JS_FALSE; } /* The opcodes stack must contain real bytecodes that index js_CodeSpec. */ ss->offsets[top] = off; ss->opcodes[top] = (op == JSOP_GETPROP2) ? JSOP_GETPROP : (op == JSOP_GETELEM2) ? JSOP_GETELEM : (jsbytecode) op; ss->top = ++top; memset(OFF2STR(&ss->sprinter, ss->sprinter.offset), 0, PAREN_SLOP); ss->sprinter.offset += PAREN_SLOP; return JS_TRUE; } static ptrdiff_t PopOff(SprintStack *ss, JSOp op) { uintN top; const JSCodeSpec *cs, *topcs; ptrdiff_t off; /* ss->top points to the next free slot; be paranoid about underflow. */ top = ss->top; JS_ASSERT(top != 0); if (top == 0) return 0; ss->top = --top; off = GetOff(ss, top); topcs = &js_CodeSpec[ss->opcodes[top]]; cs = &js_CodeSpec[op]; if (topcs->prec != 0 && topcs->prec < cs->prec) { ss->sprinter.offset = ss->offsets[top] = off - 2; off = Sprint(&ss->sprinter, "(%s)", OFF2STR(&ss->sprinter, off)); } else { ss->sprinter.offset = off; } return off; } static const char * PopStr(SprintStack *ss, JSOp op) { ptrdiff_t off; off = PopOff(ss, op); return OFF2STR(&ss->sprinter, off); } typedef struct TableEntry { jsval key; ptrdiff_t offset; JSAtom *label; jsint order; /* source order for stable tableswitch sort */ } TableEntry; static JSBool CompareOffsets(void *arg, const void *v1, const void *v2, int *result) { ptrdiff_t offset_diff; const TableEntry *te1 = (const TableEntry *) v1, *te2 = (const TableEntry *) v2; offset_diff = te1->offset - te2->offset; *result = (offset_diff == 0 ? te1->order - te2->order : offset_diff < 0 ? -1 : 1); return JS_TRUE; } static jsbytecode * Decompile(SprintStack *ss, jsbytecode *pc, intN nb); static JSBool DecompileSwitch(SprintStack *ss, TableEntry *table, uintN tableLength, jsbytecode *pc, ptrdiff_t switchLength, ptrdiff_t defaultOffset, JSBool isCondSwitch) { JSContext *cx; JSPrinter *jp; ptrdiff_t off, off2, diff, caseExprOff; char *lval, *rval; uintN i; jsval key; JSString *str; cx = ss->sprinter.context; jp = ss->printer; /* JSOP_CONDSWITCH doesn't pop, unlike JSOP_{LOOKUP,TABLE}SWITCH. */ off = isCondSwitch ? GetOff(ss, ss->top-1) : PopOff(ss, JSOP_NOP); lval = OFF2STR(&ss->sprinter, off); js_printf(CLEAR_MAYBE_BRACE(jp), "\tswitch (%s) {\n", lval); if (tableLength) { diff = table[0].offset - defaultOffset; if (diff > 0) { jp->indent += 2; js_printf(jp, "\t%s:\n", js_default_str); jp->indent += 2; if (!Decompile(ss, pc + defaultOffset, diff)) return JS_FALSE; jp->indent -= 4; } caseExprOff = isCondSwitch ? JSOP_CONDSWITCH_LENGTH : 0; for (i = 0; i < tableLength; i++) { off = table[i].offset; off2 = (i + 1 < tableLength) ? table[i + 1].offset : switchLength; key = table[i].key; if (isCondSwitch) { ptrdiff_t nextCaseExprOff; /* * key encodes the JSOP_CASE bytecode's offset from switchtop. * The next case expression follows immediately, unless we are * at the last case. */ nextCaseExprOff = (ptrdiff_t)JSVAL_TO_INT(key); nextCaseExprOff += js_CodeSpec[pc[nextCaseExprOff]].length; jp->indent += 2; if (!Decompile(ss, pc + caseExprOff, nextCaseExprOff - caseExprOff)) { return JS_FALSE; } caseExprOff = nextCaseExprOff; /* Balance the stack as if this JSOP_CASE matched. */ --ss->top; } else { /* * key comes from an atom, not the decompiler, so we need to * quote it if it's a string literal. But if table[i].label * is non-null, key was constant-propagated and label is the * name of the const we should show as the case label. We set * key to undefined so this identifier is escaped, if required * by non-ASCII characters, but not quoted, by QuoteString. */ if (table[i].label) { str = ATOM_TO_STRING(table[i].label); key = JSVAL_VOID; } else { str = js_ValueToString(cx, key); if (!str) return JS_FALSE; } rval = QuoteString(&ss->sprinter, str, (jschar)(JSVAL_IS_STRING(key) ? '"' : 0)); if (!rval) return JS_FALSE; RETRACT(&ss->sprinter, rval); jp->indent += 2; js_printf(jp, "\tcase %s:\n", rval); } jp->indent += 2; if (off <= defaultOffset && defaultOffset < off2) { diff = defaultOffset - off; if (diff != 0) { if (!Decompile(ss, pc + off, diff)) return JS_FALSE; off = defaultOffset; } jp->indent -= 2; js_printf(jp, "\t%s:\n", js_default_str); jp->indent += 2; } if (!Decompile(ss, pc + off, off2 - off)) return JS_FALSE; jp->indent -= 4; /* Re-balance as if last JSOP_CASE or JSOP_DEFAULT mismatched. */ if (isCondSwitch) ++ss->top; } } if (defaultOffset == switchLength) { jp->indent += 2; js_printf(jp, "\t%s:;\n", js_default_str); jp->indent -= 2; } js_printf(jp, "\t}\n"); /* By the end of a JSOP_CONDSWITCH, the discriminant has been popped. */ if (isCondSwitch) --ss->top; return JS_TRUE; } static JSAtom * GetSlotAtom(JSPrinter *jp, JSPropertyOp getter, uintN slot) { JSScope *scope; JSScopeProperty *sprop; JSObject *obj, *proto; scope = jp->scope; while (scope) { for (sprop = SCOPE_LAST_PROP(scope); sprop; sprop = sprop->parent) { if (sprop->getter != getter) continue; JS_ASSERT(sprop->flags & SPROP_HAS_SHORTID); JS_ASSERT(JSID_IS_ATOM(sprop->id)); if ((uintN) sprop->shortid == slot) return JSID_TO_ATOM(sprop->id); } obj = scope->object; if (!obj) break; proto = OBJ_GET_PROTO(jp->sprinter.context, obj); if (!proto) break; scope = OBJ_SCOPE(proto); } return NULL; } /* * NB: Indexed by SRC_DECL_* defines from jsemit.h. */ static const char * const var_prefix[] = {"var ", "const ", "let "}; static const char * VarPrefix(jssrcnote *sn) { if (sn && (SN_TYPE(sn) == SRC_DECL || SN_TYPE(sn) == SRC_GROUPASSIGN)) { ptrdiff_t type = js_GetSrcNoteOffset(sn, 0); if ((uintN)type <= SRC_DECL_LET) return var_prefix[type]; } return ""; } #define LOCAL_ASSERT_RV(expr, rv) \ JS_BEGIN_MACRO \ JS_ASSERT(expr); \ if (!(expr)) return (rv); \ JS_END_MACRO const char * GetLocal(SprintStack *ss, jsint i) { ptrdiff_t off; JSContext *cx; JSScript *script; jsatomid j, n; JSAtom *atom; JSObject *obj; jsint depth, count; JSScopeProperty *sprop; const char *rval; #define LOCAL_ASSERT(expr) LOCAL_ASSERT_RV(expr, "") off = ss->offsets[i]; if (off >= 0) return OFF2STR(&ss->sprinter, off); /* * We must be called from js_DecompileValueGenerator (via Decompile) when * dereferencing a local that's undefined or null. Search script->atomMap * for the block containing this local by its stack index, i. */ cx = ss->sprinter.context; script = ss->printer->script; for (j = 0, n = script->atomMap.length; j < n; j++) { atom = script->atomMap.vector[j]; if (ATOM_IS_OBJECT(atom)) { obj = ATOM_TO_OBJECT(atom); if (OBJ_GET_CLASS(cx, obj) == &js_BlockClass) { depth = OBJ_BLOCK_DEPTH(cx, obj); count = OBJ_BLOCK_COUNT(cx, obj); if ((jsuint)(i - depth) < (jsuint)count) break; } } } LOCAL_ASSERT(j < n); i -= depth; for (sprop = OBJ_SCOPE(obj)->lastProp; sprop; sprop = sprop->parent) { if (sprop->shortid == i) break; } LOCAL_ASSERT(sprop && JSID_IS_ATOM(sprop->id)); atom = JSID_TO_ATOM(sprop->id); rval = QuoteString(&ss->sprinter, ATOM_TO_STRING(atom), 0); if (!rval) return NULL; RETRACT(&ss->sprinter, rval); return rval; #undef LOCAL_ASSERT } #if JS_HAS_DESTRUCTURING #define LOCAL_ASSERT(expr) LOCAL_ASSERT_RV(expr, NULL) #define LOAD_OP_DATA(pc) (oplen = (cs = &js_CodeSpec[op = *pc])->length) static jsbytecode * DecompileDestructuring(SprintStack *ss, jsbytecode *pc, jsbytecode *endpc); static jsbytecode * DecompileDestructuringLHS(SprintStack *ss, jsbytecode *pc, jsbytecode *endpc, JSBool *hole) { JSContext *cx; JSPrinter *jp; JSOp op; const JSCodeSpec *cs; uintN oplen, i; const char *lval, *xval; ptrdiff_t todo; JSAtom *atom; *hole = JS_FALSE; cx = ss->sprinter.context; jp = ss->printer; LOAD_OP_DATA(pc); switch (op) { case JSOP_POP: *hole = JS_TRUE; todo = SprintPut(&ss->sprinter, ", ", 2); break; case JSOP_DUP: pc = DecompileDestructuring(ss, pc, endpc); if (!pc) return NULL; if (pc == endpc) return pc; LOAD_OP_DATA(pc); lval = PopStr(ss, JSOP_NOP); todo = SprintCString(&ss->sprinter, lval); if (op == JSOP_SETSP) return pc; LOCAL_ASSERT(*pc == JSOP_POP); break; case JSOP_SETARG: case JSOP_SETVAR: case JSOP_SETGVAR: case JSOP_SETLOCAL: LOCAL_ASSERT(pc[oplen] == JSOP_POP || pc[oplen] == JSOP_SETSP); /* FALL THROUGH */ case JSOP_SETLOCALPOP: i = GET_UINT16(pc); atom = NULL; lval = NULL; if (op == JSOP_SETARG) atom = GetSlotAtom(jp, js_GetArgument, i); else if (op == JSOP_SETVAR) atom = GetSlotAtom(jp, js_GetLocalVariable, i); else if (op == JSOP_SETGVAR) atom = GET_ATOM(cx, jp->script, pc); else lval = GetLocal(ss, i); if (atom) lval = js_AtomToPrintableString(cx, atom); LOCAL_ASSERT(lval); todo = SprintCString(&ss->sprinter, lval); if (op != JSOP_SETLOCALPOP) { pc += oplen; if (pc == endpc) return pc; LOAD_OP_DATA(pc); if (op == JSOP_SETSP) return pc; LOCAL_ASSERT(op == JSOP_POP); } break; default: /* * We may need to auto-parenthesize the left-most value decompiled * here, so add back PAREN_SLOP temporarily. Then decompile until the * opcode that would reduce the stack depth to (ss->top-1), which we * pass to Decompile encoded as -(ss->top-1) - 1 or just -ss->top for * the nb parameter. */ todo = ss->sprinter.offset; ss->sprinter.offset = todo + PAREN_SLOP; pc = Decompile(ss, pc, -ss->top); if (!pc) return NULL; if (pc == endpc) return pc; LOAD_OP_DATA(pc); LOCAL_ASSERT(op == JSOP_ENUMELEM || op == JSOP_ENUMCONSTELEM); xval = PopStr(ss, JSOP_NOP); lval = PopStr(ss, JSOP_GETPROP); ss->sprinter.offset = todo; if (*lval == '\0') { /* lval is from JSOP_BINDNAME, so just print xval. */ todo = SprintCString(&ss->sprinter, xval); } else if (*xval == '\0') { /* xval is from JSOP_SETCALL or JSOP_BINDXMLNAME, print lval. */ todo = SprintCString(&ss->sprinter, lval); } else { todo = Sprint(&ss->sprinter, (js_CodeSpec[ss->opcodes[ss->top+1]].format & JOF_XMLNAME) ? "%s.%s" : "%s[%s]", lval, xval); } break; } if (todo < 0) return NULL; LOCAL_ASSERT(pc < endpc); pc += oplen; return pc; } /* * Starting with a SRC_DESTRUCT-annotated JSOP_DUP, decompile a destructuring * left-hand side object or array initialiser, including nested destructuring * initialisers. On successful return, the decompilation will be pushed on ss * and the return value will point to the POP or GROUP bytecode following the * destructuring expression. * * At any point, if pc is equal to endpc and would otherwise advance, we stop * immediately and return endpc. */ static jsbytecode * DecompileDestructuring(SprintStack *ss, jsbytecode *pc, jsbytecode *endpc) { ptrdiff_t head, todo; JSContext *cx; JSPrinter *jp; JSOp op, saveop; const JSCodeSpec *cs; uintN oplen; jsint i, lasti; jsdouble d; const char *lval; jsbytecode *pc2; jsatomid atomIndex; JSAtom *atom; jssrcnote *sn; JSString *str; JSBool hole; LOCAL_ASSERT(*pc == JSOP_DUP); pc += JSOP_DUP_LENGTH; /* * Set head so we can rewrite '[' to '{' as needed. Back up PAREN_SLOP * chars so the destructuring decompilation accumulates contiguously in * ss->sprinter starting with "[". */ head = SprintPut(&ss->sprinter, "[", 1); if (head < 0 || !PushOff(ss, head, JSOP_NOP)) return NULL; ss->sprinter.offset -= PAREN_SLOP; LOCAL_ASSERT(head == ss->sprinter.offset - 1); LOCAL_ASSERT(*OFF2STR(&ss->sprinter, head) == '['); cx = ss->sprinter.context; jp = ss->printer; lasti = -1; while (pc < endpc) { LOAD_OP_DATA(pc); saveop = op; switch (op) { case JSOP_POP: pc += oplen; goto out; /* Handle the optimized number-pushing opcodes. */ case JSOP_ZERO: d = i = 0; goto do_getelem; case JSOP_ONE: d = i = 1; goto do_getelem; case JSOP_UINT16: d = i = GET_UINT16(pc); goto do_getelem; case JSOP_UINT24: d = i = GET_UINT24(pc); goto do_getelem; /* Handle the extended literal form of JSOP_NUMBER. */ case JSOP_LITOPX: atomIndex = GET_LITERAL_INDEX(pc); pc2 = pc + 1 + LITERAL_INDEX_LEN; op = *pc2; LOCAL_ASSERT(op == JSOP_NUMBER); goto do_getatom; case JSOP_NUMBER: atomIndex = GET_ATOM_INDEX(pc); do_getatom: atom = js_GetAtom(cx, &jp->script->atomMap, atomIndex); d = *ATOM_TO_DOUBLE(atom); LOCAL_ASSERT(JSDOUBLE_IS_FINITE(d) && !JSDOUBLE_IS_NEGZERO(d)); i = (jsint)d; do_getelem: sn = js_GetSrcNote(jp->script, pc); pc += oplen; if (pc == endpc) return pc; LOAD_OP_DATA(pc); LOCAL_ASSERT(op == JSOP_GETELEM); /* Distinguish object from array by opcode or source note. */ if (saveop == JSOP_LITERAL || (sn && SN_TYPE(sn) == SRC_INITPROP)) { *OFF2STR(&ss->sprinter, head) = '{'; if (Sprint(&ss->sprinter, "%g: ", d) < 0) return NULL; } else { /* Sanity check for the gnarly control flow above. */ LOCAL_ASSERT(i == d); /* Fill in any holes (holes at the end don't matter). */ while (++lasti < i) { if (SprintPut(&ss->sprinter, ", ", 2) < 0) return NULL; } } break; case JSOP_LITERAL: atomIndex = GET_LITERAL_INDEX(pc); goto do_getatom; case JSOP_GETPROP: *OFF2STR(&ss->sprinter, head) = '{'; atom = GET_ATOM(cx, jp->script, pc); str = ATOM_TO_STRING(atom); if (!QuoteString(&ss->sprinter, str, js_IsIdentifier(str) ? 0 : (jschar)'\'')) { return NULL; } if (SprintPut(&ss->sprinter, ": ", 2) < 0) return NULL; break; default: LOCAL_ASSERT(0); } pc += oplen; if (pc == endpc) return pc; /* * Decompile the left-hand side expression whose bytecode starts at pc * and continues for a bounded number of bytecodes or stack operations * (and which in any event stops before endpc). */ pc = DecompileDestructuringLHS(ss, pc, endpc, &hole); if (!pc) return NULL; if (pc == endpc || *pc != JSOP_DUP) break; /* * Check for SRC_DESTRUCT on this JSOP_DUP, which would mean another * destructuring initialiser abuts this one, and we should stop. This * happens with source of the form '[a] = [b] = c'. */ sn = js_GetSrcNote(jp->script, pc); if (sn && SN_TYPE(sn) == SRC_DESTRUCT) break; if (!hole && SprintPut(&ss->sprinter, ", ", 2) < 0) return NULL; pc += JSOP_DUP_LENGTH; } out: lval = OFF2STR(&ss->sprinter, head); todo = SprintPut(&ss->sprinter, (*lval == '[') ? "]" : "}", 1); if (todo < 0) return NULL; return pc; } static jsbytecode * DecompileGroupAssignment(SprintStack *ss, jsbytecode *pc, jsbytecode *endpc, jssrcnote *sn, ptrdiff_t *todop) { JSOp op; const JSCodeSpec *cs; uintN oplen, start, end, i; ptrdiff_t todo; JSBool hole; const char *rval; LOAD_OP_DATA(pc); LOCAL_ASSERT(op == JSOP_PUSH || op == JSOP_GETLOCAL); todo = Sprint(&ss->sprinter, "%s[", VarPrefix(sn)); if (todo < 0 || !PushOff(ss, todo, JSOP_NOP)) return NULL; ss->sprinter.offset -= PAREN_SLOP; for (;;) { pc += oplen; if (pc == endpc) return pc; pc = DecompileDestructuringLHS(ss, pc, endpc, &hole); if (!pc) return NULL; if (pc == endpc) return pc; LOAD_OP_DATA(pc); if (op != JSOP_PUSH && op != JSOP_GETLOCAL) break; if (!hole && SprintPut(&ss->sprinter, ", ", 2) < 0) return NULL; } LOCAL_ASSERT(op == JSOP_SETSP); if (SprintPut(&ss->sprinter, "] = [", 5) < 0) return NULL; start = GET_UINT16(pc); end = ss->top - 1; for (i = start; i < end; i++) { rval = GetStr(ss, i); if (Sprint(&ss->sprinter, "%s%s", (i == start) ? "" : ", ", (i == end - 1 && *rval == '\0') ? ", " : rval) < 0) { return NULL; } } if (SprintPut(&ss->sprinter, "]", 1) < 0) return NULL; ss->sprinter.offset = ss->offsets[i]; ss->top = start; *todop = todo; return pc; } #undef LOCAL_ASSERT #undef LOAD_OP_DATA #endif /* JS_HAS_DESTRUCTURING */ /* * If nb is non-negative, decompile nb bytecodes starting at pc. Otherwise * the decompiler starts at pc and continues until it reaches an opcode for * which decompiling would result in the stack depth equaling -(nb + 1). */ static jsbytecode * Decompile(SprintStack *ss, jsbytecode *pc, intN nb) { JSContext *cx; JSPrinter *jp, *jp2; jsbytecode *startpc, *endpc, *pc2, *done, *forelem_tail, *forelem_done; ptrdiff_t tail, todo, len, oplen, cond, next; JSOp op, lastop, saveop; const JSCodeSpec *cs; jssrcnote *sn, *sn2; const char *lval, *rval, *xval, *fmt; jsint i, argc; char **argv; jsatomid atomIndex; JSAtom *atom; JSObject *obj; JSFunction *fun; JSString *str; JSBool ok; #if JS_HAS_XML_SUPPORT JSBool foreach, inXML, quoteAttr; #else #define inXML JS_FALSE #endif jsval val; int stackDummy; static const char exception_cookie[] = "/*EXCEPTION*/"; static const char retsub_pc_cookie[] = "/*RETSUB_PC*/"; static const char forelem_cookie[] = "/*FORELEM*/"; static const char with_cookie[] = "/*WITH*/"; static const char dot_format[] = "%s.%s"; static const char index_format[] = "%s[%s]"; static const char predot_format[] = "%s%s.%s"; static const char postdot_format[] = "%s.%s%s"; static const char preindex_format[] = "%s%s[%s]"; static const char postindex_format[] = "%s[%s]%s"; static const char ss_format[] = "%s%s"; /* * Local macros */ #define DECOMPILE_CODE(pc,nb) if (!Decompile(ss, pc, nb)) return NULL #define POP_STR() PopStr(ss, op) #define LOCAL_ASSERT(expr) LOCAL_ASSERT_RV(expr, JS_FALSE) /* * Callers know that ATOM_IS_STRING(atom), and we leave it to the optimizer to * common ATOM_TO_STRING(atom) here and near the call sites. */ #define ATOM_IS_IDENTIFIER(atom) js_IsIdentifier(ATOM_TO_STRING(atom)) #define ATOM_IS_KEYWORD(atom) \ (js_CheckKeyword(JSSTRING_CHARS(ATOM_TO_STRING(atom)), \ JSSTRING_LENGTH(ATOM_TO_STRING(atom))) != TOK_EOF) /* * Given an atom already fetched from jp->script's atom map, quote/escape its * string appropriately into rval, and select fmt from the quoted and unquoted * alternatives. */ #define GET_QUOTE_AND_FMT(qfmt, ufmt, rval) \ JS_BEGIN_MACRO \ jschar quote_; \ if (!ATOM_IS_IDENTIFIER(atom)) { \ quote_ = '\''; \ fmt = qfmt; \ } else { \ quote_ = 0; \ fmt = ufmt; \ } \ rval = QuoteString(&ss->sprinter, ATOM_TO_STRING(atom), quote_); \ if (!rval) \ return NULL; \ JS_END_MACRO /* * Get atom from jp->script's atom map, quote/escape its string appropriately * into rval, and select fmt from the quoted and unquoted alternatives. */ #define GET_ATOM_QUOTE_AND_FMT(qfmt, ufmt, rval) \ JS_BEGIN_MACRO \ atom = GET_ATOM(cx, jp->script, pc); \ GET_QUOTE_AND_FMT(qfmt, ufmt, rval); \ JS_END_MACRO cx = ss->sprinter.context; if (!JS_CHECK_STACK_SIZE(cx, stackDummy)) { JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_OVER_RECURSED); return NULL; } jp = ss->printer; startpc = pc; endpc = (nb < 0) ? jp->script->code + jp->script->length : pc + nb; forelem_tail = forelem_done = NULL; tail = -1; todo = -2; /* NB: different from Sprint() error return. */ saveop = JSOP_NOP; sn = NULL; rval = NULL; #if JS_HAS_XML_SUPPORT foreach = inXML = quoteAttr = JS_FALSE; #endif while (nb < 0 || pc < endpc) { /* * Move saveop to lastop so prefixed bytecodes can take special action * while sharing maximal code. Set op and saveop to the new bytecode, * use op in POP_STR to trigger automatic parenthesization, but push * saveop at the bottom of the loop if this op pushes. Thus op may be * set to nop or otherwise mutated to suppress auto-parens. */ lastop = saveop; op = saveop = (JSOp) *pc; cs = &js_CodeSpec[saveop]; len = oplen = cs->length; if (nb < 0 && -(nb + 1) == (intN)ss->top - cs->nuses + cs->ndefs) return pc; if (pc + oplen == jp->dvgfence) { JSStackFrame *fp; uint32 format, mode, type; /* * Rewrite non-get ops to their "get" format if the error is in * the bytecode at pc, so we don't decompile more than the error * expression. */ for (fp = cx->fp; fp && !fp->script; fp = fp->down) continue; format = cs->format; if (((fp && pc == fp->pc) || (pc == startpc && cs->nuses != 0)) && format & (JOF_SET|JOF_DEL|JOF_INCDEC|JOF_IMPORT|JOF_FOR)) { mode = (format & JOF_MODEMASK); if (mode == JOF_NAME) { /* * JOF_NAME does not imply JOF_CONST, so we must check for * the QARG and QVAR format types, and translate those to * JSOP_GETARG or JSOP_GETVAR appropriately, instead of to * JSOP_NAME. */ type = format & JOF_TYPEMASK; op = (type == JOF_QARG) ? JSOP_GETARG : (type == JOF_QVAR) ? JSOP_GETVAR : (type == JOF_LOCAL) ? JSOP_GETLOCAL : JSOP_NAME; i = cs->nuses - js_CodeSpec[op].nuses; while (--i >= 0) PopOff(ss, JSOP_NOP); } else { /* * We must replace the faulting pc's bytecode with a * corresponding JSOP_GET* code. For JSOP_SET{PROP,ELEM}, * we must use the "2nd" form of JSOP_GET{PROP,ELEM}, to * throw away the assignment op's right-hand operand and * decompile it as if it were a GET of its left-hand * operand. */ if (mode == JOF_PROP) { op = (format & JOF_SET) ? JSOP_GETPROP2 : JSOP_GETPROP; } else if (mode == JOF_ELEM) { op = (format & JOF_SET) ? JSOP_GETELEM2 : JSOP_GETELEM; } else { /* * Zero mode means precisely that op is uncategorized * for our purposes, so we must write per-op special * case code here. */ switch (op) { case JSOP_ENUMELEM: case JSOP_ENUMCONSTELEM: op = JSOP_GETELEM; break; #if JS_HAS_LVALUE_RETURN case JSOP_SETCALL: op = JSOP_CALL; break; #endif default: LOCAL_ASSERT(0); } } } } saveop = op; if (op >= JSOP_LIMIT) { switch (op) { case JSOP_GETPROP2: saveop = JSOP_GETPROP; break; case JSOP_GETELEM2: saveop = JSOP_GETELEM; break; default:; } } LOCAL_ASSERT(js_CodeSpec[saveop].length == oplen); jp->dvgfence = NULL; } if (cs->token) { switch (cs->nuses) { case 2: sn = js_GetSrcNote(jp->script, pc); if (sn && SN_TYPE(sn) == SRC_ASSIGNOP) { /* * Avoid over-parenthesizing y in x op= y based on its * expansion: x = x op y (replace y by z = w to see the * problem). */ op = pc[oplen]; LOCAL_ASSERT(op != saveop); } rval = POP_STR(); lval = POP_STR(); if (op != saveop) { /* Print only the right operand of the assignment-op. */ todo = SprintCString(&ss->sprinter, rval); op = saveop; } else if (!inXML) { todo = Sprint(&ss->sprinter, "%s %s %s", lval, cs->token, rval); } else { /* In XML, just concatenate the two operands. */ LOCAL_ASSERT(op == JSOP_ADD); todo = Sprint(&ss->sprinter, ss_format, lval, rval); } break; case 1: rval = POP_STR(); todo = Sprint(&ss->sprinter, ss_format, cs->token, rval); break; case 0: todo = SprintCString(&ss->sprinter, cs->token); break; default: todo = -2; break; } } else { switch (op) { #define BEGIN_LITOPX_CASE(OP) \ case OP: \ atomIndex = GET_ATOM_INDEX(pc); \ do_##OP: \ atom = js_GetAtom(cx, &jp->script->atomMap, atomIndex); #define END_LITOPX_CASE \ break; case JSOP_NOP: /* * Check for a do-while loop, a for-loop with an empty * initializer part, a labeled statement, a function * definition, or try/finally. */ sn = js_GetSrcNote(jp->script, pc); todo = -2; switch (sn ? SN_TYPE(sn) : SRC_NULL) { case SRC_WHILE: js_printf(SET_MAYBE_BRACE(jp), "\tdo {\n"); jp->indent += 4; break; case SRC_FOR: rval = ""; do_forloop: /* Skip the JSOP_NOP or JSOP_POP bytecode. */ pc++; /* Get the cond, next, and loop-closing tail offsets. */ cond = js_GetSrcNoteOffset(sn, 0); next = js_GetSrcNoteOffset(sn, 1); tail = js_GetSrcNoteOffset(sn, 2); LOCAL_ASSERT(tail + GetJumpOffset(pc+tail, pc+tail) == 0); /* Print the keyword and the possibly empty init-part. */ js_printf(jp, "\tfor (%s;", rval); if (pc[cond] == JSOP_IFEQ || pc[cond] == JSOP_IFEQX) { /* Decompile the loop condition. */ DECOMPILE_CODE(pc, cond); js_printf(jp, " %s", POP_STR()); } /* Need a semicolon whether or not there was a cond. */ js_puts(jp, ";"); if (pc[next] != JSOP_GOTO && pc[next] != JSOP_GOTOX) { /* Decompile the loop updater. */ DECOMPILE_CODE(pc + next, tail - next - 1); js_printf(jp, " %s", POP_STR()); } /* Do the loop body. */ js_printf(SET_MAYBE_BRACE(jp), ") {\n"); jp->indent += 4; oplen = (cond) ? js_CodeSpec[pc[cond]].length : 0; DECOMPILE_CODE(pc + cond + oplen, next - cond - oplen); jp->indent -= 4; js_printf(jp, "\t}\n"); /* Set len so pc skips over the entire loop. */ len = tail + js_CodeSpec[pc[tail]].length; break; case SRC_LABEL: atom = js_GetAtom(cx, &jp->script->atomMap, (jsatomid) js_GetSrcNoteOffset(sn, 0)); jp->indent -= 4; rval = QuoteString(&ss->sprinter, ATOM_TO_STRING(atom), 0); if (!rval) return NULL; RETRACT(&ss->sprinter, rval); js_printf(CLEAR_MAYBE_BRACE(jp), "\t%s:\n", rval); jp->indent += 4; break; case SRC_LABELBRACE: atom = js_GetAtom(cx, &jp->script->atomMap, (jsatomid) js_GetSrcNoteOffset(sn, 0)); rval = QuoteString(&ss->sprinter, ATOM_TO_STRING(atom), 0); if (!rval) return NULL; RETRACT(&ss->sprinter, rval); js_printf(CLEAR_MAYBE_BRACE(jp), "\t%s: {\n", rval); jp->indent += 4; break; case SRC_ENDBRACE: jp->indent -= 4; js_printf(jp, "\t}\n"); break; case SRC_FUNCDEF: atom = js_GetAtom(cx, &jp->script->atomMap, (jsatomid) js_GetSrcNoteOffset(sn, 0)); LOCAL_ASSERT(ATOM_IS_OBJECT(atom)); do_function: obj = ATOM_TO_OBJECT(atom); fun = (JSFunction *) JS_GetPrivate(cx, obj); jp2 = js_NewPrinter(cx, JS_GetFunctionName(fun), jp->indent, jp->pretty); if (!jp2) return NULL; jp2->scope = jp->scope; js_puts(jp2, "\n"); ok = js_DecompileFunction(jp2, fun); if (ok) { js_puts(jp2, "\n"); str = js_GetPrinterOutput(jp2); if (str) js_printf(jp, "%s\n", JS_GetStringBytes(str)); else ok = JS_FALSE; } js_DestroyPrinter(jp2); if (!ok) return NULL; break; case SRC_BRACE: js_printf(CLEAR_MAYBE_BRACE(jp), "\t{\n"); jp->indent += 4; len = js_GetSrcNoteOffset(sn, 0); DECOMPILE_CODE(pc + oplen, len - oplen); jp->indent -= 4; js_printf(jp, "\t}\n"); break; default:; } break; case JSOP_GROUP: cs = &js_CodeSpec[lastop]; if ((cs->prec != 0 && cs->prec == js_CodeSpec[pc[JSOP_GROUP_LENGTH]].prec) || pc[JSOP_GROUP_LENGTH] == JSOP_PUSHOBJ || pc[JSOP_GROUP_LENGTH] == JSOP_DUP) { /* * Force parens if this JSOP_GROUP forced re-association * against precedence, or if this is a call or constructor * expression, or if it is destructured (JSOP_DUP). * * This is necessary to handle the operator new grammar, * by which new x(y).z means (new x(y))).z. For example * new (x(y).z) must decompile with the constructor * parenthesized, but normal precedence has JSOP_GETPROP * (for the final .z) higher than JSOP_NEW. In general, * if the call or constructor expression is parenthesized, * we preserve parens. */ op = JSOP_NAME; rval = POP_STR(); todo = SprintCString(&ss->sprinter, rval); } else { /* * Don't explicitly parenthesize -- just fix the top * opcode so that the auto-parens magic in PopOff can do * its thing. */ LOCAL_ASSERT(ss->top != 0); ss->opcodes[ss->top-1] = saveop = lastop; todo = -2; } break; case JSOP_STARTITER: todo = -2; break; case JSOP_PUSH: #if JS_HAS_DESTRUCTURING sn = js_GetSrcNote(jp->script, pc); if (sn && SN_TYPE(sn) == SRC_GROUPASSIGN) { pc = DecompileGroupAssignment(ss, pc, endpc, sn, &todo); if (!pc) return NULL; LOCAL_ASSERT(*pc == JSOP_SETSP); len = oplen = JSOP_SETSP_LENGTH; goto end_groupassignment; } #endif /* FALL THROUGH */ case JSOP_PUSHOBJ: case JSOP_BINDNAME: do_JSOP_BINDNAME: todo = Sprint(&ss->sprinter, ""); break; case JSOP_TRY: js_printf(CLEAR_MAYBE_BRACE(jp), "\ttry {\n"); jp->indent += 4; todo = -2; break; case JSOP_FINALLY: jp->indent -= 4; js_printf(CLEAR_MAYBE_BRACE(jp), "\t} finally {\n"); jp->indent += 4; /* * We must push an empty string placeholder for gosub's return * address, popped by JSOP_RETSUB and counted by script->depth * but not by ss->top (see JSOP_SETSP, below). */ todo = Sprint(&ss->sprinter, exception_cookie); if (todo < 0 || !PushOff(ss, todo, op)) return NULL; todo = Sprint(&ss->sprinter, retsub_pc_cookie); break; case JSOP_RETSUB: rval = POP_STR(); LOCAL_ASSERT(strcmp(rval, retsub_pc_cookie) == 0); lval = POP_STR(); LOCAL_ASSERT(strcmp(lval, exception_cookie) == 0); todo = -2; break; case JSOP_SWAP: /* * We don't generate this opcode currently, and previously we * did not need to decompile it. If old, serialized bytecode * uses it still, we should fall through and set todo = -2. */ /* FALL THROUGH */ case JSOP_GOSUB: case JSOP_GOSUBX: /* * JSOP_GOSUB and GOSUBX have no effect on the decompiler's * string stack because the next op in bytecode order finds * the stack balanced by a JSOP_RETSUB executed elsewhere. */ todo = -2; break; case JSOP_SETSP: { uintN newtop, oldtop, i; /* * The compiler models operand stack depth and fixes the stack * pointer on entry to a catch clause based on its depth model. * The decompiler must match the code generator's model, which * is why JSOP_FINALLY pushes a cookie that JSOP_RETSUB pops. */ newtop = (uintN) GET_UINT16(pc); oldtop = ss->top; LOCAL_ASSERT(newtop <= oldtop); todo = -2; #if JS_HAS_DESTRUCTURING sn = js_GetSrcNote(jp->script, pc); if (sn && SN_TYPE(sn) == SRC_GROUPASSIGN) { todo = Sprint(&ss->sprinter, "%s[] = [", VarPrefix(sn)); if (todo < 0) return NULL; for (i = newtop; i < oldtop; i++) { rval = OFF2STR(&ss->sprinter, ss->offsets[i]); if (Sprint(&ss->sprinter, ss_format, (i == newtop) ? "" : ", ", (i == oldtop - 1 && *rval == '\0') ? ", " : rval) < 0) { return NULL; } } if (SprintPut(&ss->sprinter, "]", 1) < 0) return NULL; /* * Kill newtop before the end_groupassignment: label by * retracting/popping early. Control will either jump to * do_forloop: or do_letheadbody: or else break from our * case JSOP_SETSP: after the switch (*pc2) below. */ if (newtop < oldtop) { ss->sprinter.offset = GetOff(ss, newtop); ss->top = newtop; } end_groupassignment: /* * Thread directly to the next opcode if we can, to handle * the special cases of a group assignment in the first or * last part of a for(;;) loop head, or in a let block or * expression head. * * NB: todo at this point indexes space in ss->sprinter * that is liable to be overwritten. The code below knows * exactly how long rval lives, or else copies it down via * SprintCString. */ rval = OFF2STR(&ss->sprinter, todo); todo = -2; pc2 = pc + oplen; switch (*pc2) { case JSOP_NOP: /* First part of for(;;) or let block/expr head. */ sn = js_GetSrcNote(jp->script, pc2); if (sn) { if (SN_TYPE(sn) == SRC_FOR) { pc = pc2; goto do_forloop; } if (SN_TYPE(sn) == SRC_DECL) { if (ss->top == jp->script->depth) { /* * This must be an empty destructuring * in the head of a let whose body block * is also empty. */ pc = pc2 + 1; len = js_GetSrcNoteOffset(sn, 0); LOCAL_ASSERT(pc[len] == JSOP_LEAVEBLOCK); js_printf(jp, "\tlet (%s) {\n", rval); js_printf(jp, "\t}\n"); goto end_setsp; } todo = SprintCString(&ss->sprinter, rval); if (todo < 0 || !PushOff(ss, todo, JSOP_NOP)) return NULL; op = JSOP_POP; pc = pc2 + 1; goto do_letheadbody; } } break; case JSOP_GOTO: case JSOP_GOTOX: /* Third part of for(;;) loop head. */ cond = GetJumpOffset(pc2, pc2); sn = js_GetSrcNote(jp->script, pc2 + cond - 1); if (sn && SN_TYPE(sn) == SRC_FOR) { todo = SprintCString(&ss->sprinter, rval); saveop = JSOP_NOP; } break; } /* * If control flow reaches this point with todo still -2, * just print rval as an expression statement. */ if (todo == -2) js_printf(jp, "\t%s;\n", rval); end_setsp: break; } #endif if (newtop < oldtop) { ss->sprinter.offset = GetOff(ss, newtop); ss->top = newtop; } break; } case JSOP_EXCEPTION: /* The catch decompiler handles this op itself. */ LOCAL_ASSERT(JS_FALSE); break; case JSOP_POP: /* * By default, do not automatically parenthesize when popping * a stacked expression decompilation. We auto-parenthesize * only when JSOP_POP is annotated with SRC_PCDELTA, meaning * comma operator. */ op = JSOP_POPV; /* FALL THROUGH */ case JSOP_POPV: sn = js_GetSrcNote(jp->script, pc); switch (sn ? SN_TYPE(sn) : SRC_NULL) { case SRC_FOR: /* Force parens around 'in' expression at 'for' front. */ if (ss->opcodes[ss->top-1] == JSOP_IN) op = JSOP_LSH; rval = POP_STR(); todo = -2; goto do_forloop; case SRC_PCDELTA: /* Comma operator: use JSOP_POP for correct precedence. */ op = JSOP_POP; /* Pop and save to avoid blowing stack depth budget. */ lval = JS_strdup(cx, POP_STR()); if (!lval) return NULL; /* * The offset tells distance to the end of the right-hand * operand of the comma operator. */ done = pc + len; pc += js_GetSrcNoteOffset(sn, 0); len = 0; if (!Decompile(ss, done, pc - done)) { JS_free(cx, (char *)lval); return NULL; } /* Pop Decompile result and print comma expression. */ rval = POP_STR(); todo = Sprint(&ss->sprinter, "%s, %s", lval, rval); JS_free(cx, (char *)lval); break; case SRC_HIDDEN: /* Hide this pop, it's from a goto in a with or for/in. */ todo = -2; break; case SRC_DECL: /* This pop is at the end of the let block/expr head. */ pc += JSOP_POP_LENGTH; #if JS_HAS_DESTRUCTURING do_letheadbody: #endif len = js_GetSrcNoteOffset(sn, 0); if (pc[len] == JSOP_LEAVEBLOCK) { js_printf(CLEAR_MAYBE_BRACE(jp), "\tlet (%s) {\n", POP_STR()); jp->indent += 4; DECOMPILE_CODE(pc, len); jp->indent -= 4; js_printf(jp, "\t}\n"); todo = -2; } else { LOCAL_ASSERT(pc[len] == JSOP_LEAVEBLOCKEXPR); lval = JS_strdup(cx, POP_STR()); if (!lval) return NULL; if (!Decompile(ss, pc, len)) { JS_free(cx, (char *)lval); return NULL; } rval = POP_STR(); todo = Sprint(&ss->sprinter, (*rval == '{') ? "let (%s) (%s)" : "let (%s) %s", lval, rval); JS_free(cx, (char *)lval); } break; default: /* Turn off parens around a yield statement. */ if (ss->opcodes[ss->top-1] == JSOP_YIELD) op = JSOP_NOP; rval = POP_STR(); if (*rval != '\0') { #if JS_HAS_BLOCK_SCOPE /* * If a let declaration is the only child of a control * structure that does not require braces, it must not * be braced. If it were braced explicitly, it would * be bracketed by JSOP_ENTERBLOCK/JSOP_LEAVEBLOCK. */ if (jp->braceState == MAYBE_BRACE && pc + JSOP_POP_LENGTH == endpc && !strncmp(rval, var_prefix[SRC_DECL_LET], 4) && rval[4] != '(') { SetDontBrace(jp); } #endif js_printf(jp, (*rval == '{' || (strncmp(rval, js_function_str, 8) == 0 && rval[8] == ' ')) ? "\t(%s);\n" : "\t%s;\n", rval); } todo = -2; break; } break; case JSOP_POP2: case JSOP_ENDITER: sn = js_GetSrcNote(jp->script, pc); todo = -2; if (sn && SN_TYPE(sn) == SRC_HIDDEN) break; (void) PopOff(ss, op); if (op == JSOP_POP2) (void) PopOff(ss, op); break; case JSOP_ENTERWITH: LOCAL_ASSERT(!js_GetSrcNote(jp->script, pc)); rval = POP_STR(); js_printf(SET_MAYBE_BRACE(jp), "\twith (%s) {\n", rval); jp->indent += 4; todo = Sprint(&ss->sprinter, with_cookie); break; case JSOP_LEAVEWITH: sn = js_GetSrcNote(jp->script, pc); todo = -2; if (sn && SN_TYPE(sn) == SRC_HIDDEN) break; rval = POP_STR(); LOCAL_ASSERT(strcmp(rval, with_cookie) == 0); jp->indent -= 4; js_printf(jp, "\t}\n"); break; BEGIN_LITOPX_CASE(JSOP_ENTERBLOCK) { JSAtom **atomv, *smallv[5]; JSScopeProperty *sprop; obj = ATOM_TO_OBJECT(atom); argc = OBJ_BLOCK_COUNT(cx, obj); if ((size_t)argc <= sizeof smallv / sizeof smallv[0]) { atomv = smallv; } else { atomv = (JSAtom **) JS_malloc(cx, argc * sizeof(JSAtom *)); if (!atomv) return NULL; } /* From here on, control must flow through enterblock_out. */ for (sprop = OBJ_SCOPE(obj)->lastProp; sprop; sprop = sprop->parent) { if (!(sprop->flags & SPROP_HAS_SHORTID)) continue; LOCAL_ASSERT(sprop->shortid < argc); atomv[sprop->shortid] = JSID_TO_ATOM(sprop->id); } ok = JS_TRUE; for (i = 0; i < argc; i++) { atom = atomv[i]; rval = QuoteString(&ss->sprinter, ATOM_TO_STRING(atom), 0); if (!rval || !PushOff(ss, STR2OFF(&ss->sprinter, rval), op)) { ok = JS_FALSE; goto enterblock_out; } } sn = js_GetSrcNote(jp->script, pc); switch (sn ? SN_TYPE(sn) : SRC_NULL) { #if JS_HAS_BLOCK_SCOPE case SRC_BRACE: js_printf(CLEAR_MAYBE_BRACE(jp), "\t{\n"); jp->indent += 4; len = js_GetSrcNoteOffset(sn, 0); ok = Decompile(ss, pc + oplen, len - oplen) != NULL; if (!ok) goto enterblock_out; jp->indent -= 4; js_printf(jp, "\t}\n"); break; #endif case SRC_CATCH: jp->indent -= 4; js_printf(CLEAR_MAYBE_BRACE(jp), "\t} catch ("); pc2 = pc; pc += oplen; LOCAL_ASSERT(*pc == JSOP_EXCEPTION); pc += JSOP_EXCEPTION_LENGTH; if (*pc == JSOP_DUP) { sn2 = js_GetSrcNote(jp->script, pc); if (sn2 && SN_TYPE(sn2) == SRC_HIDDEN) { /* * This is a hidden dup to save the exception for * later. It must exist only when the catch has * an exception guard. */ LOCAL_ASSERT(js_GetSrcNoteOffset(sn, 0) != 0); pc += JSOP_DUP_LENGTH; } } #if JS_HAS_DESTRUCTURING if (*pc == JSOP_DUP) { pc = DecompileDestructuring(ss, pc, endpc); if (!pc) { ok = JS_FALSE; goto enterblock_out; } LOCAL_ASSERT(*pc == JSOP_POP); pc += JSOP_POP_LENGTH; lval = PopStr(ss, JSOP_NOP); js_puts(jp, lval); } else { #endif LOCAL_ASSERT(*pc == JSOP_SETLOCALPOP); i = GET_UINT16(pc); pc += JSOP_SETLOCALPOP_LENGTH; atom = atomv[i - OBJ_BLOCK_DEPTH(cx, obj)]; str = ATOM_TO_STRING(atom); if (!QuoteString(&jp->sprinter, str, 0)) { ok = JS_FALSE; goto enterblock_out; } #if JS_HAS_DESTRUCTURING } #endif len = js_GetSrcNoteOffset(sn, 0); if (len) { len -= PTRDIFF(pc, pc2, jsbytecode); LOCAL_ASSERT(len > 0); js_printf(jp, " if "); ok = Decompile(ss, pc, len) != NULL; if (!ok) goto enterblock_out; js_printf(jp, "%s", POP_STR()); pc += len; LOCAL_ASSERT(*pc == JSOP_IFEQ || *pc == JSOP_IFEQX); pc += js_CodeSpec[*pc].length; } js_printf(jp, ") {\n"); jp->indent += 4; len = 0; break; } todo = -2; enterblock_out: if (atomv != smallv) JS_free(cx, atomv); if (!ok) return NULL; } END_LITOPX_CASE case JSOP_LEAVEBLOCK: case JSOP_LEAVEBLOCKEXPR: { uintN top, depth; sn = js_GetSrcNote(jp->script, pc); todo = -2; if (op == JSOP_LEAVEBLOCKEXPR) { LOCAL_ASSERT(SN_TYPE(sn) == SRC_PCBASE); rval = POP_STR(); } else if (sn) { LOCAL_ASSERT(op == JSOP_LEAVEBLOCK); if (SN_TYPE(sn) == SRC_HIDDEN) break; LOCAL_ASSERT(SN_TYPE(sn) == SRC_CATCH); LOCAL_ASSERT((uintN)js_GetSrcNoteOffset(sn, 0) == ss->top); } top = ss->top; depth = GET_UINT16(pc); LOCAL_ASSERT(top >= depth); top -= depth; ss->top = top; ss->sprinter.offset = GetOff(ss, top); if (op == JSOP_LEAVEBLOCKEXPR) todo = SprintCString(&ss->sprinter, rval); break; } case JSOP_GETLOCAL: i = GET_UINT16(pc); sn = js_GetSrcNote(jp->script, pc); LOCAL_ASSERT((uintN)i < ss->top); rval = GetLocal(ss, i); #if JS_HAS_DESTRUCTURING if (sn && SN_TYPE(sn) == SRC_GROUPASSIGN) { pc = DecompileGroupAssignment(ss, pc, endpc, sn, &todo); if (!pc) return NULL; LOCAL_ASSERT(*pc == JSOP_SETSP); len = oplen = JSOP_SETSP_LENGTH; goto end_groupassignment; } #endif todo = Sprint(&ss->sprinter, ss_format, VarPrefix(sn), rval); break; case JSOP_SETLOCAL: case JSOP_SETLOCALPOP: i = GET_UINT16(pc); lval = GetStr(ss, i); rval = POP_STR(); goto do_setlval; case JSOP_INCLOCAL: case JSOP_DECLOCAL: i = GET_UINT16(pc); lval = GetLocal(ss, i); goto do_inclval; case JSOP_LOCALINC: case JSOP_LOCALDEC: i = GET_UINT16(pc); lval = GetLocal(ss, i); goto do_lvalinc; case JSOP_FORLOCAL: i = GET_UINT16(pc); lval = GetStr(ss, i); atom = NULL; goto do_forlvalinloop; case JSOP_RETRVAL: todo = -2; break; case JSOP_SETRVAL: case JSOP_RETURN: rval = POP_STR(); if (*rval != '\0') js_printf(jp, "\t%s %s;\n", js_return_str, rval); else js_printf(jp, "\t%s;\n", js_return_str); todo = -2; break; #if JS_HAS_GENERATORS case JSOP_YIELD: op = JSOP_SETNAME; /* turn off most parens */ rval = POP_STR(); todo = (*rval != '\0') ? Sprint(&ss->sprinter, (strncmp(rval, js_yield_str, 5) == 0 && (rval[5] == ' ' || rval[5] == '\0')) ? "%s (%s)" : "%s %s", js_yield_str, rval) : SprintCString(&ss->sprinter, js_yield_str); break; case JSOP_ARRAYPUSH: { uintN pos, blockpos, startpos; ptrdiff_t start; rval = POP_STR(); pos = ss->top; while ((op = ss->opcodes[--pos]) != JSOP_ENTERBLOCK && op != JSOP_NEWINIT) { LOCAL_ASSERT(pos != 0); } blockpos = pos; while (ss->opcodes[pos] == JSOP_ENTERBLOCK) { if (pos == 0) break; --pos; } LOCAL_ASSERT(ss->opcodes[pos] == JSOP_NEWINIT); startpos = pos; start = ss->offsets[pos]; LOCAL_ASSERT(ss->sprinter.base[start] == '[' || ss->sprinter.base[start] == '#'); pos = blockpos; while (ss->opcodes[++pos] == JSOP_STARTITER) LOCAL_ASSERT(pos < ss->top); LOCAL_ASSERT(pos < ss->top); xval = OFF2STR(&ss->sprinter, ss->offsets[pos]); lval = OFF2STR(&ss->sprinter, start); RETRACT(&ss->sprinter, lval); todo = Sprint(&ss->sprinter, "%s%s%.*s", lval, rval, rval - xval, xval); if (todo < 0) return NULL; ss->offsets[startpos] = todo; todo = -2; break; } #endif case JSOP_THROWING: todo = -2; break; case JSOP_THROW: sn = js_GetSrcNote(jp->script, pc); todo = -2; if (sn && SN_TYPE(sn) == SRC_HIDDEN) break; rval = POP_STR(); js_printf(jp, "\t%s %s;\n", cs->name, rval); break; case JSOP_GOTO: case JSOP_GOTOX: sn = js_GetSrcNote(jp->script, pc); switch (sn ? SN_TYPE(sn) : SRC_NULL) { case SRC_CONT2LABEL: atom = js_GetAtom(cx, &jp->script->atomMap, (jsatomid) js_GetSrcNoteOffset(sn, 0)); rval = QuoteString(&ss->sprinter, ATOM_TO_STRING(atom), 0); if (!rval) return NULL; RETRACT(&ss->sprinter, rval); js_printf(jp, "\tcontinue %s;\n", rval); break; case SRC_CONTINUE: js_printf(jp, "\tcontinue;\n"); break; case SRC_BREAK2LABEL: atom = js_GetAtom(cx, &jp->script->atomMap, (jsatomid) js_GetSrcNoteOffset(sn, 0)); rval = QuoteString(&ss->sprinter, ATOM_TO_STRING(atom), 0); if (!rval) return NULL; RETRACT(&ss->sprinter, rval); js_printf(jp, "\tbreak %s;\n", rval); break; case SRC_HIDDEN: break; default: js_printf(jp, "\tbreak;\n"); break; } todo = -2; break; case JSOP_IFEQ: case JSOP_IFEQX: { JSBool elseif = JS_FALSE; if_again: len = GetJumpOffset(pc, pc); sn = js_GetSrcNote(jp->script, pc); switch (sn ? SN_TYPE(sn) : SRC_NULL) { case SRC_IF: case SRC_IF_ELSE: op = JSOP_NOP; /* turn off parens */ rval = POP_STR(); if (ss->inArrayInit) { LOCAL_ASSERT(SN_TYPE(sn) == SRC_IF); if (Sprint(&ss->sprinter, " if (%s)", rval) < 0) return NULL; } else { js_printf(SET_MAYBE_BRACE(jp), elseif ? " if (%s) {\n" : "\tif (%s) {\n", rval); jp->indent += 4; } if (SN_TYPE(sn) == SRC_IF) { DECOMPILE_CODE(pc + oplen, len - oplen); } else { LOCAL_ASSERT(!ss->inArrayInit); tail = js_GetSrcNoteOffset(sn, 0); DECOMPILE_CODE(pc + oplen, tail - oplen); jp->indent -= 4; pc += tail; LOCAL_ASSERT(*pc == JSOP_GOTO || *pc == JSOP_GOTOX); oplen = js_CodeSpec[*pc].length; len = GetJumpOffset(pc, pc); js_printf(jp, "\t} else"); /* * If the second offset for sn is non-zero, it tells * the distance from the goto around the else, to the * ifeq for the if inside the else that forms an "if * else if" chain. Thus cond spans the condition of * the second if, so we simply decompile it and start * over at label if_again. */ cond = js_GetSrcNoteOffset(sn, 1); if (cond != 0) { DECOMPILE_CODE(pc + oplen, cond - oplen); pc += cond; elseif = JS_TRUE; goto if_again; } js_printf(SET_MAYBE_BRACE(jp), " {\n"); jp->indent += 4; DECOMPILE_CODE(pc + oplen, len - oplen); } if (!ss->inArrayInit) { jp->indent -= 4; js_printf(jp, "\t}\n"); } todo = -2; break; case SRC_WHILE: rval = POP_STR(); js_printf(SET_MAYBE_BRACE(jp), "\twhile (%s) {\n", rval); jp->indent += 4; tail = js_GetSrcNoteOffset(sn, 0); DECOMPILE_CODE(pc + oplen, tail - oplen); jp->indent -= 4; js_printf(jp, "\t}\n"); todo = -2; break; case SRC_COND: xval = JS_strdup(cx, POP_STR()); if (!xval) return NULL; len = js_GetSrcNoteOffset(sn, 0); DECOMPILE_CODE(pc + oplen, len - oplen); lval = JS_strdup(cx, POP_STR()); if (!lval) { JS_free(cx, (void *)xval); return NULL; } pc += len; LOCAL_ASSERT(*pc == JSOP_GOTO || *pc == JSOP_GOTOX); oplen = js_CodeSpec[*pc].length; len = GetJumpOffset(pc, pc); DECOMPILE_CODE(pc + oplen, len - oplen); rval = POP_STR(); todo = Sprint(&ss->sprinter, "%s ? %s : %s", xval, lval, rval); JS_free(cx, (void *)xval); JS_free(cx, (void *)lval); break; default: break; } break; } case JSOP_IFNE: case JSOP_IFNEX: /* Currently, this must be a do-while loop's upward branch. */ jp->indent -= 4; js_printf(jp, "\t} while (%s);\n", POP_STR()); todo = -2; break; case JSOP_OR: case JSOP_ORX: xval = "||"; do_logical_connective: /* Top of stack is the first clause in a disjunction (||). */ lval = JS_strdup(cx, POP_STR()); if (!lval) return NULL; done = pc + GetJumpOffset(pc, pc); pc += len; len = PTRDIFF(done, pc, jsbytecode); DECOMPILE_CODE(pc, len); rval = POP_STR(); if (jp->pretty && jp->indent + 4 + strlen(lval) + 4 + strlen(rval) > 75) { rval = JS_strdup(cx, rval); if (!rval) { tail = -1; } else { todo = Sprint(&ss->sprinter, "%s %s\n", lval, xval); tail = Sprint(&ss->sprinter, "%*s%s", jp->indent + 4, "", rval); JS_free(cx, (char *)rval); } if (tail < 0) todo = -1; } else { todo = Sprint(&ss->sprinter, "%s %s %s", lval, xval, rval); } JS_free(cx, (char *)lval); break; case JSOP_AND: case JSOP_ANDX: xval = "&&"; goto do_logical_connective; case JSOP_FORARG: atom = GetSlotAtom(jp, js_GetArgument, GET_ARGNO(pc)); LOCAL_ASSERT(atom); goto do_fornameinloop; case JSOP_FORVAR: atom = GetSlotAtom(jp, js_GetLocalVariable, GET_VARNO(pc)); LOCAL_ASSERT(atom); goto do_fornameinloop; case JSOP_FORNAME: atom = GET_ATOM(cx, jp->script, pc); do_fornameinloop: lval = ""; do_forlvalinloop: sn = js_GetSrcNote(jp->script, pc); xval = NULL; goto do_forinloop; case JSOP_FORPROP: xval = NULL; atom = GET_ATOM(cx, jp->script, pc); if (!ATOM_IS_IDENTIFIER(atom)) { xval = QuoteString(&ss->sprinter, ATOM_TO_STRING(atom), (jschar)'\''); if (!xval) return NULL; atom = NULL; } lval = POP_STR(); sn = NULL; do_forinloop: pc += oplen; LOCAL_ASSERT(*pc == JSOP_IFEQ || *pc == JSOP_IFEQX); oplen = js_CodeSpec[*pc].length; len = GetJumpOffset(pc, pc); sn2 = js_GetSrcNote(jp->script, pc); tail = js_GetSrcNoteOffset(sn2, 0); do_forinhead: if (!atom && xval) { /* * If xval is not a dummy empty string, we have to strdup * it to save it from being clobbered by the first Sprint * below. Standard dumb decompiler operating procedure! */ if (*xval == '\0') { xval = NULL; } else { xval = JS_strdup(cx, xval); if (!xval) return NULL; } } #if JS_HAS_XML_SUPPORT if (foreach) { foreach = JS_FALSE; todo = Sprint(&ss->sprinter, "for %s (%s%s", js_each_str, VarPrefix(sn), lval); } else #endif { todo = Sprint(&ss->sprinter, "for (%s%s", VarPrefix(sn), lval); } if (atom) { if (*lval && SprintPut(&ss->sprinter, ".", 1) < 0) return NULL; xval = QuoteString(&ss->sprinter, ATOM_TO_STRING(atom), 0); if (!xval) return NULL; } else if (xval) { LOCAL_ASSERT(*xval != '\0'); ok = (Sprint(&ss->sprinter, (js_CodeSpec[lastop].format & JOF_XMLNAME) ? ".%s" : "[%s]", xval) >= 0); JS_free(cx, (char *)xval); if (!ok) return NULL; } if (todo < 0) return NULL; lval = OFF2STR(&ss->sprinter, todo); rval = GetStr(ss, ss->top-1); RETRACT(&ss->sprinter, rval); if (ss->inArrayInit) { todo = Sprint(&ss->sprinter, " %s in %s)", lval, rval); if (todo < 0) return NULL; ss->offsets[ss->top-1] = todo; ss->sprinter.offset += PAREN_SLOP; DECOMPILE_CODE(pc + oplen, tail - oplen); } else { js_printf(SET_MAYBE_BRACE(jp), "\t%s in %s) {\n", lval, rval); jp->indent += 4; DECOMPILE_CODE(pc + oplen, tail - oplen); jp->indent -= 4; js_printf(jp, "\t}\n"); } todo = -2; break; case JSOP_FORELEM: pc++; LOCAL_ASSERT(*pc == JSOP_IFEQ || *pc == JSOP_IFEQX); len = js_CodeSpec[*pc].length; /* * Arrange for the JSOP_ENUMELEM case to set tail for use by * do_forinhead: code that uses on it to find the loop-closing * jump (whatever its format, normal or extended), in order to * bound the recursively decompiled loop body. */ sn = js_GetSrcNote(jp->script, pc); LOCAL_ASSERT(!forelem_tail); forelem_tail = pc + js_GetSrcNoteOffset(sn, 0); /* * This gets a little wacky. Only the length of the for loop * body PLUS the element-indexing expression is known here, so * we pass the after-loop pc to the JSOP_ENUMELEM case, which * is immediately below, to decompile that helper bytecode via * the 'forelem_done' local. * * Since a for..in loop can't nest in the head of another for * loop, we can use forelem_{tail,done} singletons to remember * state from JSOP_FORELEM to JSOP_ENUMELEM, thence (via goto) * to label do_forinhead. */ LOCAL_ASSERT(!forelem_done); forelem_done = pc + GetJumpOffset(pc, pc); /* Our net stack balance after forelem;ifeq is +1. */ todo = SprintCString(&ss->sprinter, forelem_cookie); break; case JSOP_ENUMELEM: case JSOP_ENUMCONSTELEM: /* * The stack has the object under the (top) index expression. * The "rval" property id is underneath those two on the stack. * The for loop body net and gross lengths can now be adjusted * to account for the length of the indexing expression that * came after JSOP_FORELEM and before JSOP_ENUMELEM. */ atom = NULL; xval = POP_STR(); op = JSOP_GETELEM; /* lval must have high precedence */ lval = POP_STR(); op = saveop; rval = POP_STR(); LOCAL_ASSERT(strcmp(rval, forelem_cookie) == 0); LOCAL_ASSERT(forelem_tail > pc); tail = forelem_tail - pc; forelem_tail = NULL; LOCAL_ASSERT(forelem_done > pc); len = forelem_done - pc; forelem_done = NULL; goto do_forinhead; #if JS_HAS_GETTER_SETTER case JSOP_GETTER: case JSOP_SETTER: todo = -2; break; #endif case JSOP_DUP2: rval = GetStr(ss, ss->top-2); todo = SprintCString(&ss->sprinter, rval); if (todo < 0 || !PushOff(ss, todo, ss->opcodes[ss->top-2])) return NULL; /* FALL THROUGH */ case JSOP_DUP: #if JS_HAS_DESTRUCTURING sn = js_GetSrcNote(jp->script, pc); if (sn) { LOCAL_ASSERT(SN_TYPE(sn) == SRC_DESTRUCT); pc = DecompileDestructuring(ss, pc, endpc); if (!pc) return NULL; len = 0; lval = POP_STR(); op = saveop = JSOP_ENUMELEM; rval = POP_STR(); if (strcmp(rval, forelem_cookie) == 0) { LOCAL_ASSERT(forelem_tail > pc); tail = forelem_tail - pc; forelem_tail = NULL; LOCAL_ASSERT(forelem_done > pc); len = forelem_done - pc; forelem_done = NULL; xval = NULL; atom = NULL; /* * Null sn if this is a 'for (var [k, v] = i in o)' * loop, because 'var [k, v = i;' has already been * hoisted. */ if (js_GetSrcNoteOffset(sn, 0) == SRC_DECL_VAR) sn = NULL; goto do_forinhead; } todo = Sprint(&ss->sprinter, "%s%s = %s", VarPrefix(sn), lval, rval); break; } #endif rval = GetStr(ss, ss->top-1); saveop = ss->opcodes[ss->top-1]; todo = SprintCString(&ss->sprinter, rval); break; case JSOP_SETARG: atom = GetSlotAtom(jp, js_GetArgument, GET_ARGNO(pc)); LOCAL_ASSERT(atom); goto do_setname; case JSOP_SETVAR: atom = GetSlotAtom(jp, js_GetLocalVariable, GET_VARNO(pc)); LOCAL_ASSERT(atom); goto do_setname; case JSOP_SETCONST: case JSOP_SETNAME: case JSOP_SETGVAR: atomIndex = GET_ATOM_INDEX(pc); do_JSOP_SETCONST: atom = js_GetAtom(cx, &jp->script->atomMap, atomIndex); do_setname: lval = QuoteString(&ss->sprinter, ATOM_TO_STRING(atom), 0); if (!lval) return NULL; rval = POP_STR(); if (op == JSOP_SETNAME) (void) PopOff(ss, op); do_setlval: sn = js_GetSrcNote(jp->script, pc - 1); if (sn && SN_TYPE(sn) == SRC_ASSIGNOP) { todo = Sprint(&ss->sprinter, "%s %s= %s", lval, (lastop == JSOP_GETTER) ? js_getter_str : (lastop == JSOP_SETTER) ? js_setter_str : js_CodeSpec[lastop].token, rval); } else { sn = js_GetSrcNote(jp->script, pc); todo = Sprint(&ss->sprinter, "%s%s = %s", VarPrefix(sn), lval, rval); } if (op == JSOP_SETLOCALPOP) { if (!PushOff(ss, todo, saveop)) return NULL; rval = POP_STR(); LOCAL_ASSERT(*rval != '\0'); js_printf(jp, "\t%s;\n", rval); todo = -2; } break; case JSOP_NEW: case JSOP_CALL: case JSOP_EVAL: #if JS_HAS_LVALUE_RETURN case JSOP_SETCALL: #endif op = JSOP_SETNAME; /* turn off most parens */ argc = GET_ARGC(pc); argv = (char **) JS_malloc(cx, (size_t)(argc + 1) * sizeof *argv); if (!argv) return NULL; ok = JS_TRUE; for (i = argc; i > 0; i--) { argv[i] = JS_strdup(cx, POP_STR()); if (!argv[i]) { ok = JS_FALSE; break; } } /* Skip the JSOP_PUSHOBJ-created empty string. */ LOCAL_ASSERT(ss->top >= 2); (void) PopOff(ss, op); op = saveop; argv[0] = JS_strdup(cx, POP_STR()); if (!argv[i]) ok = JS_FALSE; lval = "(", rval = ")"; if (op == JSOP_NEW) { if (argc == 0) lval = rval = ""; todo = Sprint(&ss->sprinter, "%s %s%s", js_new_str, argv[0], lval); } else { todo = Sprint(&ss->sprinter, ss_format, argv[0], lval); } if (todo < 0) ok = JS_FALSE; for (i = 1; i <= argc; i++) { if (!argv[i] || Sprint(&ss->sprinter, ss_format, argv[i], (i < argc) ? ", " : "") < 0) { ok = JS_FALSE; break; } } if (Sprint(&ss->sprinter, rval) < 0) ok = JS_FALSE; for (i = 0; i <= argc; i++) { if (argv[i]) JS_free(cx, argv[i]); } JS_free(cx, argv); if (!ok) return NULL; #if JS_HAS_LVALUE_RETURN if (op == JSOP_SETCALL) { if (!PushOff(ss, todo, op)) return NULL; todo = Sprint(&ss->sprinter, ""); } #endif break; case JSOP_DELNAME: atom = GET_ATOM(cx, jp->script, pc); lval = QuoteString(&ss->sprinter, ATOM_TO_STRING(atom), 0); if (!lval) return NULL; RETRACT(&ss->sprinter, lval); do_delete_lval: todo = Sprint(&ss->sprinter, "%s %s", js_delete_str, lval); break; case JSOP_DELPROP: GET_ATOM_QUOTE_AND_FMT("%s %s[%s]", "%s %s.%s", rval); lval = POP_STR(); todo = Sprint(&ss->sprinter, fmt, js_delete_str, lval, rval); break; case JSOP_DELELEM: op = JSOP_NOP; /* turn off parens */ xval = POP_STR(); op = saveop; lval = POP_STR(); if (*xval == '\0') goto do_delete_lval; todo = Sprint(&ss->sprinter, (js_CodeSpec[lastop].format & JOF_XMLNAME) ? "%s %s.%s" : "%s %s[%s]", js_delete_str, lval, xval); break; #if JS_HAS_XML_SUPPORT case JSOP_DELDESC: xval = POP_STR(); lval = POP_STR(); todo = Sprint(&ss->sprinter, "%s %s..%s", js_delete_str, lval, xval); break; #endif case JSOP_TYPEOFEXPR: case JSOP_TYPEOF: case JSOP_VOID: rval = POP_STR(); todo = Sprint(&ss->sprinter, "%s %s", cs->name, rval); break; case JSOP_INCARG: case JSOP_DECARG: atom = GetSlotAtom(jp, js_GetArgument, GET_ARGNO(pc)); LOCAL_ASSERT(atom); goto do_incatom; case JSOP_INCVAR: case JSOP_DECVAR: atom = GetSlotAtom(jp, js_GetLocalVariable, GET_VARNO(pc)); LOCAL_ASSERT(atom); goto do_incatom; case JSOP_INCNAME: case JSOP_DECNAME: case JSOP_INCGVAR: case JSOP_DECGVAR: atom = GET_ATOM(cx, jp->script, pc); do_incatom: lval = QuoteString(&ss->sprinter, ATOM_TO_STRING(atom), 0); if (!lval) return NULL; RETRACT(&ss->sprinter, lval); do_inclval: todo = Sprint(&ss->sprinter, ss_format, js_incop_strs[!(cs->format & JOF_INC)], lval); break; case JSOP_INCPROP: case JSOP_DECPROP: GET_ATOM_QUOTE_AND_FMT(preindex_format, predot_format, rval); /* * Force precedence below the numeric literal opcodes, so that * 42..foo or 10000..toString(16), e.g., decompile with parens * around the left-hand side of dot. */ op = JSOP_GETPROP; lval = POP_STR(); todo = Sprint(&ss->sprinter, fmt, js_incop_strs[!(cs->format & JOF_INC)], lval, rval); break; case JSOP_INCELEM: case JSOP_DECELEM: op = JSOP_NOP; /* turn off parens */ xval = POP_STR(); op = JSOP_GETELEM; lval = POP_STR(); if (*xval != '\0') { todo = Sprint(&ss->sprinter, (js_CodeSpec[lastop].format & JOF_XMLNAME) ? predot_format : preindex_format, js_incop_strs[!(cs->format & JOF_INC)], lval, xval); } else { todo = Sprint(&ss->sprinter, ss_format, js_incop_strs[!(cs->format & JOF_INC)], lval); } break; case JSOP_ARGINC: case JSOP_ARGDEC: atom = GetSlotAtom(jp, js_GetArgument, GET_ARGNO(pc)); LOCAL_ASSERT(atom); goto do_atominc; case JSOP_VARINC: case JSOP_VARDEC: atom = GetSlotAtom(jp, js_GetLocalVariable, GET_VARNO(pc)); LOCAL_ASSERT(atom); goto do_atominc; case JSOP_NAMEINC: case JSOP_NAMEDEC: case JSOP_GVARINC: case JSOP_GVARDEC: atom = GET_ATOM(cx, jp->script, pc); do_atominc: lval = QuoteString(&ss->sprinter, ATOM_TO_STRING(atom), 0); if (!lval) return NULL; RETRACT(&ss->sprinter, lval); do_lvalinc: todo = Sprint(&ss->sprinter, ss_format, lval, js_incop_strs[!(cs->format & JOF_INC)]); break; case JSOP_PROPINC: case JSOP_PROPDEC: GET_ATOM_QUOTE_AND_FMT(postindex_format, postdot_format, rval); /* * Force precedence below the numeric literal opcodes, so that * 42..foo or 10000..toString(16), e.g., decompile with parens * around the left-hand side of dot. */ op = JSOP_GETPROP; lval = POP_STR(); todo = Sprint(&ss->sprinter, fmt, lval, rval, js_incop_strs[!(cs->format & JOF_INC)]); break; case JSOP_ELEMINC: case JSOP_ELEMDEC: op = JSOP_NOP; /* turn off parens */ xval = POP_STR(); op = JSOP_GETELEM; lval = POP_STR(); if (*xval != '\0') { todo = Sprint(&ss->sprinter, (js_CodeSpec[lastop].format & JOF_XMLNAME) ? postdot_format : postindex_format, lval, xval, js_incop_strs[!(cs->format & JOF_INC)]); } else { todo = Sprint(&ss->sprinter, ss_format, lval, js_incop_strs[!(cs->format & JOF_INC)]); } break; case JSOP_GETPROP2: op = JSOP_GETPROP; (void) PopOff(ss, lastop); /* FALL THROUGH */ case JSOP_GETPROP: case JSOP_GETXPROP: atom = GET_ATOM(cx, jp->script, pc); do_getprop: GET_QUOTE_AND_FMT(index_format, dot_format, rval); do_getprop_lval: lval = POP_STR(); todo = Sprint(&ss->sprinter, fmt, lval, rval); break; #if JS_HAS_XML_SUPPORT BEGIN_LITOPX_CASE(JSOP_GETMETHOD) sn = js_GetSrcNote(jp->script, pc); if (sn && SN_TYPE(sn) == SRC_PCBASE) goto do_getprop; GET_QUOTE_AND_FMT("%s.function::[%s]", "%s.function::%s", rval); goto do_getprop_lval; BEGIN_LITOPX_CASE(JSOP_SETMETHOD) sn = js_GetSrcNote(jp->script, pc); if (sn && SN_TYPE(sn) == SRC_PCBASE) goto do_setprop; GET_QUOTE_AND_FMT("%s.function::[%s] %s= %s", "%s.function::%s %s= %s", xval); goto do_setprop_rval; #endif case JSOP_SETPROP: atom = GET_ATOM(cx, jp->script, pc); do_setprop: GET_QUOTE_AND_FMT("%s[%s] %s= %s", "%s.%s %s= %s", xval); do_setprop_rval: rval = POP_STR(); /* * Force precedence below the numeric literal opcodes, so that * 42..foo or 10000..toString(16), e.g., decompile with parens * around the left-hand side of dot. */ op = JSOP_GETPROP; lval = POP_STR(); sn = js_GetSrcNote(jp->script, pc - 1); todo = Sprint(&ss->sprinter, fmt, lval, xval, (sn && SN_TYPE(sn) == SRC_ASSIGNOP) ? (lastop == JSOP_GETTER) ? js_getter_str : (lastop == JSOP_SETTER) ? js_setter_str : js_CodeSpec[lastop].token : "", rval); break; case JSOP_GETELEM2: op = JSOP_GETELEM; (void) PopOff(ss, lastop); /* FALL THROUGH */ case JSOP_GETELEM: case JSOP_GETXELEM: op = JSOP_NOP; /* turn off parens */ xval = POP_STR(); op = saveop; lval = POP_STR(); if (*xval == '\0') { todo = Sprint(&ss->sprinter, "%s", lval); } else { todo = Sprint(&ss->sprinter, (js_CodeSpec[lastop].format & JOF_XMLNAME) ? dot_format : index_format, lval, xval); } break; case JSOP_SETELEM: rval = POP_STR(); op = JSOP_NOP; /* turn off parens */ xval = POP_STR(); cs = &js_CodeSpec[ss->opcodes[ss->top]]; op = JSOP_GETELEM; /* lval must have high precedence */ lval = POP_STR(); op = saveop; if (*xval == '\0') goto do_setlval; sn = js_GetSrcNote(jp->script, pc - 1); todo = Sprint(&ss->sprinter, (cs->format & JOF_XMLNAME) ? "%s.%s %s= %s" : "%s[%s] %s= %s", lval, xval, (sn && SN_TYPE(sn) == SRC_ASSIGNOP) ? (lastop == JSOP_GETTER) ? js_getter_str : (lastop == JSOP_SETTER) ? js_setter_str : js_CodeSpec[lastop].token : "", rval); break; case JSOP_ARGSUB: i = (jsint) GET_ATOM_INDEX(pc); todo = Sprint(&ss->sprinter, "%s[%d]", js_arguments_str, (int) i); break; case JSOP_ARGCNT: todo = Sprint(&ss->sprinter, dot_format, js_arguments_str, js_length_str); break; case JSOP_GETARG: i = GET_ARGNO(pc); atom = GetSlotAtom(jp, js_GetArgument, i); #if JS_HAS_DESTRUCTURING if (!atom) { todo = Sprint(&ss->sprinter, "%s[%d]", js_arguments_str, i); break; } #else LOCAL_ASSERT(atom); #endif goto do_name; case JSOP_GETVAR: atom = GetSlotAtom(jp, js_GetLocalVariable, GET_VARNO(pc)); LOCAL_ASSERT(atom); goto do_name; case JSOP_NAME: case JSOP_GETGVAR: atom = GET_ATOM(cx, jp->script, pc); do_name: lval = ""; do_qname: sn = js_GetSrcNote(jp->script, pc); rval = QuoteString(&ss->sprinter, ATOM_TO_STRING(atom), 0); if (!rval) return NULL; RETRACT(&ss->sprinter, rval); todo = Sprint(&ss->sprinter, "%s%s%s", VarPrefix(sn), lval, rval); break; case JSOP_UINT16: i = (jsint) GET_ATOM_INDEX(pc); goto do_sprint_int; case JSOP_UINT24: i = (jsint) GET_UINT24(pc); do_sprint_int: todo = Sprint(&ss->sprinter, "%u", (unsigned) i); break; case JSOP_LITERAL: atomIndex = GET_LITERAL_INDEX(pc); goto do_JSOP_STRING; case JSOP_FINDNAME: atomIndex = GET_LITERAL_INDEX(pc); todo = Sprint(&ss->sprinter, ""); if (todo < 0 || !PushOff(ss, todo, op)) return NULL; atom = js_GetAtom(cx, &jp->script->atomMap, atomIndex); goto do_name; case JSOP_LITOPX: atomIndex = GET_LITERAL_INDEX(pc); pc2 = pc + 1 + LITERAL_INDEX_LEN; op = saveop = *pc2; pc += len - (1 + ATOM_INDEX_LEN); cs = &js_CodeSpec[op]; len = cs->length; switch (op) { case JSOP_ANONFUNOBJ: goto do_JSOP_ANONFUNOBJ; case JSOP_BINDNAME: goto do_JSOP_BINDNAME; case JSOP_CLOSURE: goto do_JSOP_CLOSURE; #if JS_HAS_EXPORT_IMPORT case JSOP_EXPORTNAME: goto do_JSOP_EXPORTNAME; #endif #if JS_HAS_XML_SUPPORT case JSOP_GETMETHOD: goto do_JSOP_GETMETHOD; case JSOP_SETMETHOD: goto do_JSOP_SETMETHOD; #endif case JSOP_NAMEDFUNOBJ: goto do_JSOP_NAMEDFUNOBJ; case JSOP_NUMBER: goto do_JSOP_NUMBER; case JSOP_OBJECT: goto do_JSOP_OBJECT; #if JS_HAS_XML_SUPPORT case JSOP_QNAMECONST: goto do_JSOP_QNAMECONST; case JSOP_QNAMEPART: goto do_JSOP_QNAMEPART; #endif case JSOP_REGEXP: goto do_JSOP_REGEXP; case JSOP_SETCONST: goto do_JSOP_SETCONST; case JSOP_STRING: goto do_JSOP_STRING; #if JS_HAS_XML_SUPPORT case JSOP_XMLCDATA: goto do_JSOP_XMLCDATA; case JSOP_XMLCOMMENT: goto do_JSOP_XMLCOMMENT; case JSOP_XMLOBJECT: goto do_JSOP_XMLOBJECT; case JSOP_XMLPI: goto do_JSOP_XMLPI; #endif case JSOP_ENTERBLOCK: goto do_JSOP_ENTERBLOCK; default: LOCAL_ASSERT(0); } /* NOTREACHED */ break; BEGIN_LITOPX_CASE(JSOP_NUMBER) val = ATOM_KEY(atom); if (JSVAL_IS_INT(val)) { long ival = (long)JSVAL_TO_INT(val); todo = Sprint(&ss->sprinter, "%ld", ival); } else { char buf[DTOSTR_STANDARD_BUFFER_SIZE]; char *numStr = JS_dtostr(buf, sizeof buf, DTOSTR_STANDARD, 0, *JSVAL_TO_DOUBLE(val)); if (!numStr) { JS_ReportOutOfMemory(cx); return NULL; } todo = Sprint(&ss->sprinter, numStr); } END_LITOPX_CASE BEGIN_LITOPX_CASE(JSOP_STRING) rval = QuoteString(&ss->sprinter, ATOM_TO_STRING(atom), inXML ? DONT_ESCAPE : '"'); if (!rval) return NULL; todo = STR2OFF(&ss->sprinter, rval); END_LITOPX_CASE case JSOP_OBJECT: case JSOP_REGEXP: case JSOP_ANONFUNOBJ: case JSOP_NAMEDFUNOBJ: atomIndex = GET_ATOM_INDEX(pc); do_JSOP_OBJECT: do_JSOP_REGEXP: do_JSOP_ANONFUNOBJ: do_JSOP_NAMEDFUNOBJ: atom = js_GetAtom(cx, &jp->script->atomMap, atomIndex); if (op == JSOP_OBJECT || op == JSOP_REGEXP) { if (!js_regexp_toString(cx, ATOM_TO_OBJECT(atom), 0, NULL, &val)) { return NULL; } } else { if (!js_fun_toString(cx, ATOM_TO_OBJECT(atom), JS_IN_GROUP_CONTEXT | JS_DONT_PRETTY_PRINT, 0, NULL, &val)) { return NULL; } } str = JSVAL_TO_STRING(val); todo = SprintPut(&ss->sprinter, JS_GetStringBytes(str), JSSTRING_LENGTH(str)); break; case JSOP_TABLESWITCH: case JSOP_TABLESWITCHX: { ptrdiff_t jmplen, off, off2; jsint j, n, low, high; TableEntry *table, pivot; sn = js_GetSrcNote(jp->script, pc); LOCAL_ASSERT(sn && SN_TYPE(sn) == SRC_SWITCH); len = js_GetSrcNoteOffset(sn, 0); jmplen = (op == JSOP_TABLESWITCH) ? JUMP_OFFSET_LEN : JUMPX_OFFSET_LEN; pc2 = pc; off = GetJumpOffset(pc, pc2); pc2 += jmplen; low = GET_JUMP_OFFSET(pc2); pc2 += JUMP_OFFSET_LEN; high = GET_JUMP_OFFSET(pc2); pc2 += JUMP_OFFSET_LEN; n = high - low + 1; if (n == 0) { table = NULL; j = 0; } else { table = (TableEntry *) JS_malloc(cx, (size_t)n * sizeof *table); if (!table) return NULL; for (i = j = 0; i < n; i++) { table[j].label = NULL; off2 = GetJumpOffset(pc, pc2); if (off2) { sn = js_GetSrcNote(jp->script, pc2); if (sn) { LOCAL_ASSERT(SN_TYPE(sn) == SRC_LABEL); table[j].label = js_GetAtom(cx, &jp->script->atomMap, (jsatomid) js_GetSrcNoteOffset(sn, 0)); } table[j].key = INT_TO_JSVAL(low + i); table[j].offset = off2; table[j].order = j; j++; } pc2 += jmplen; } js_HeapSort(table, (size_t) j, &pivot, sizeof(TableEntry), CompareOffsets, NULL); } ok = DecompileSwitch(ss, table, (uintN)j, pc, len, off, JS_FALSE); JS_free(cx, table); if (!ok) return NULL; todo = -2; break; } case JSOP_LOOKUPSWITCH: case JSOP_LOOKUPSWITCHX: { ptrdiff_t jmplen, off, off2; jsatomid npairs, k; TableEntry *table; sn = js_GetSrcNote(jp->script, pc); LOCAL_ASSERT(sn && SN_TYPE(sn) == SRC_SWITCH); len = js_GetSrcNoteOffset(sn, 0); jmplen = (op == JSOP_LOOKUPSWITCH) ? JUMP_OFFSET_LEN : JUMPX_OFFSET_LEN; pc2 = pc; off = GetJumpOffset(pc, pc2); pc2 += jmplen; npairs = GET_ATOM_INDEX(pc2); pc2 += ATOM_INDEX_LEN; table = (TableEntry *) JS_malloc(cx, (size_t)npairs * sizeof *table); if (!table) return NULL; for (k = 0; k < npairs; k++) { sn = js_GetSrcNote(jp->script, pc2); if (sn) { LOCAL_ASSERT(SN_TYPE(sn) == SRC_LABEL); table[k].label = js_GetAtom(cx, &jp->script->atomMap, (jsatomid) js_GetSrcNoteOffset(sn, 0)); } else { table[k].label = NULL; } atom = GET_ATOM(cx, jp->script, pc2); pc2 += ATOM_INDEX_LEN; off2 = GetJumpOffset(pc, pc2); pc2 += jmplen; table[k].key = ATOM_KEY(atom); table[k].offset = off2; } ok = DecompileSwitch(ss, table, (uintN)npairs, pc, len, off, JS_FALSE); JS_free(cx, table); if (!ok) return NULL; todo = -2; break; } case JSOP_CONDSWITCH: { ptrdiff_t off, off2, caseOff; jsint ncases; TableEntry *table; sn = js_GetSrcNote(jp->script, pc); LOCAL_ASSERT(sn && SN_TYPE(sn) == SRC_SWITCH); len = js_GetSrcNoteOffset(sn, 0); off = js_GetSrcNoteOffset(sn, 1); /* * Count the cases using offsets from switch to first case, * and case to case, stored in srcnote immediates. */ pc2 = pc; off2 = off; for (ncases = 0; off2 != 0; ncases++) { pc2 += off2; LOCAL_ASSERT(*pc2 == JSOP_CASE || *pc2 == JSOP_DEFAULT || *pc2 == JSOP_CASEX || *pc2 == JSOP_DEFAULTX); if (*pc2 == JSOP_DEFAULT || *pc2 == JSOP_DEFAULTX) { /* End of cases, but count default as a case. */ off2 = 0; } else { sn = js_GetSrcNote(jp->script, pc2); LOCAL_ASSERT(sn && SN_TYPE(sn) == SRC_PCDELTA); off2 = js_GetSrcNoteOffset(sn, 0); } } /* * Allocate table and rescan the cases using their srcnotes, * stashing each case's delta from switch top in table[i].key, * and the distance to its statements in table[i].offset. */ table = (TableEntry *) JS_malloc(cx, (size_t)ncases * sizeof *table); if (!table) return NULL; pc2 = pc; off2 = off; for (i = 0; i < ncases; i++) { pc2 += off2; LOCAL_ASSERT(*pc2 == JSOP_CASE || *pc2 == JSOP_DEFAULT || *pc2 == JSOP_CASEX || *pc2 == JSOP_DEFAULTX); caseOff = pc2 - pc; table[i].key = INT_TO_JSVAL((jsint) caseOff); table[i].offset = caseOff + GetJumpOffset(pc2, pc2); if (*pc2 == JSOP_CASE || *pc2 == JSOP_CASEX) { sn = js_GetSrcNote(jp->script, pc2); LOCAL_ASSERT(sn && SN_TYPE(sn) == SRC_PCDELTA); off2 = js_GetSrcNoteOffset(sn, 0); } } /* * Find offset of default code by fetching the default offset * from the end of table. JSOP_CONDSWITCH always has a default * case at the end. */ off = JSVAL_TO_INT(table[ncases-1].key); pc2 = pc + off; off += GetJumpOffset(pc2, pc2); ok = DecompileSwitch(ss, table, (uintN)ncases, pc, len, off, JS_TRUE); JS_free(cx, table); if (!ok) return NULL; todo = -2; break; } case JSOP_CASE: case JSOP_CASEX: { lval = POP_STR(); if (!lval) return NULL; js_printf(jp, "\tcase %s:\n", lval); todo = -2; break; } case JSOP_NEW_EQ: case JSOP_NEW_NE: rval = POP_STR(); lval = POP_STR(); todo = Sprint(&ss->sprinter, "%s %c== %s", lval, (op == JSOP_NEW_EQ) ? '=' : '!', rval); break; BEGIN_LITOPX_CASE(JSOP_CLOSURE) LOCAL_ASSERT(ATOM_IS_OBJECT(atom)); todo = -2; goto do_function; END_LITOPX_CASE #if JS_HAS_EXPORT_IMPORT case JSOP_EXPORTALL: js_printf(jp, "\texport *;\n"); todo = -2; break; BEGIN_LITOPX_CASE(JSOP_EXPORTNAME) rval = QuoteString(&ss->sprinter, ATOM_TO_STRING(atom), 0); if (!rval) return NULL; RETRACT(&ss->sprinter, rval); js_printf(jp, "\texport %s;\n", rval); todo = -2; END_LITOPX_CASE case JSOP_IMPORTALL: lval = POP_STR(); js_printf(jp, "\timport %s.*;\n", lval); todo = -2; break; case JSOP_IMPORTPROP: do_importprop: GET_ATOM_QUOTE_AND_FMT("\timport %s[%s];\n", "\timport %s.%s;\n", rval); lval = POP_STR(); js_printf(jp, fmt, lval, rval); todo = -2; break; case JSOP_IMPORTELEM: xval = POP_STR(); op = JSOP_GETELEM; if (js_CodeSpec[lastop].format & JOF_XMLNAME) goto do_importprop; lval = POP_STR(); js_printf(jp, "\timport %s[%s];\n", lval, xval); todo = -2; break; #endif /* JS_HAS_EXPORT_IMPORT */ case JSOP_TRAP: op = JS_GetTrapOpcode(cx, jp->script, pc); if (op == JSOP_LIMIT) return NULL; saveop = op; *pc = op; cs = &js_CodeSpec[op]; len = cs->length; DECOMPILE_CODE(pc, len); *pc = JSOP_TRAP; todo = -2; break; case JSOP_NEWINIT: { JSBool isArray; LOCAL_ASSERT(ss->top >= 2); (void) PopOff(ss, op); lval = POP_STR(); isArray = (*lval == 'A'); todo = ss->sprinter.offset; #if JS_HAS_SHARP_VARS op = (JSOp)pc[len]; if (op == JSOP_DEFSHARP) { pc += len; cs = &js_CodeSpec[op]; len = cs->length; i = (jsint) GET_ATOM_INDEX(pc); if (Sprint(&ss->sprinter, "#%u=", (unsigned) i) < 0) return NULL; } #endif /* JS_HAS_SHARP_VARS */ if (isArray) { ++ss->inArrayInit; if (SprintCString(&ss->sprinter, "[") < 0) return NULL; } else { if (SprintCString(&ss->sprinter, "{") < 0) return NULL; } break; } case JSOP_ENDINIT: op = JSOP_NOP; /* turn off parens */ rval = POP_STR(); sn = js_GetSrcNote(jp->script, pc); /* Skip any #n= prefix to find the opening bracket. */ for (xval = rval; *xval != '[' && *xval != '{'; xval++) continue; if (*xval == '[') --ss->inArrayInit; todo = Sprint(&ss->sprinter, "%s%s%c", rval, (sn && SN_TYPE(sn) == SRC_CONTINUE) ? ", " : "", (*xval == '[') ? ']' : '}'); break; case JSOP_INITPROP: atom = GET_ATOM(cx, jp->script, pc); xval = QuoteString(&ss->sprinter, ATOM_TO_STRING(atom), (jschar) (ATOM_IS_IDENTIFIER(atom) ? 0 : '\'')); if (!xval) return NULL; rval = POP_STR(); lval = POP_STR(); do_initprop: #ifdef OLD_GETTER_SETTER todo = Sprint(&ss->sprinter, "%s%s%s%s%s:%s", lval, (lval[1] != '\0') ? ", " : "", xval, (lastop == JSOP_GETTER || lastop == JSOP_SETTER) ? " " : "", (lastop == JSOP_GETTER) ? js_getter_str : (lastop == JSOP_SETTER) ? js_setter_str : "", rval); #else if (lastop == JSOP_GETTER || lastop == JSOP_SETTER) { if (!atom || !ATOM_IS_STRING(atom) || !ATOM_IS_IDENTIFIER(atom) || ATOM_IS_KEYWORD(atom) || ((ss->opcodes[ss->top+1] != JSOP_ANONFUNOBJ || strncmp(rval, js_function_str, 8) != 0) && ss->opcodes[ss->top+1] != JSOP_NAMEDFUNOBJ)) { todo = Sprint(&ss->sprinter, "%s%s%s%s%s:%s", lval, (lval[1] != '\0') ? ", " : "", xval, (lastop == JSOP_GETTER || lastop == JSOP_SETTER) ? " " : "", (lastop == JSOP_GETTER) ? js_getter_str : (lastop == JSOP_SETTER) ? js_setter_str : "", rval); } else { rval += 8 + 1; LOCAL_ASSERT(rval[strlen(rval)-1] == '}'); todo = Sprint(&ss->sprinter, "%s%s%s %s%s", lval, (lval[1] != '\0') ? ", " : "", (lastop == JSOP_GETTER) ? js_get_str : js_set_str, xval, rval); } } else { todo = Sprint(&ss->sprinter, "%s%s%s:%s", lval, (lval[1] != '\0') ? ", " : "", xval, rval); } #endif break; case JSOP_INITELEM: rval = POP_STR(); xval = POP_STR(); lval = POP_STR(); sn = js_GetSrcNote(jp->script, pc); if (sn && SN_TYPE(sn) == SRC_INITPROP) { atom = NULL; goto do_initprop; } todo = Sprint(&ss->sprinter, "%s%s%s", lval, (lval[1] != '\0' || *xval != '0') ? ", " : "", rval); break; #if JS_HAS_SHARP_VARS case JSOP_DEFSHARP: i = (jsint) GET_ATOM_INDEX(pc); rval = POP_STR(); todo = Sprint(&ss->sprinter, "#%u=%s", (unsigned) i, rval); break; case JSOP_USESHARP: i = (jsint) GET_ATOM_INDEX(pc); todo = Sprint(&ss->sprinter, "#%u#", (unsigned) i); break; #endif /* JS_HAS_SHARP_VARS */ #if JS_HAS_DEBUGGER_KEYWORD case JSOP_DEBUGGER: js_printf(jp, "\tdebugger;\n"); todo = -2; break; #endif /* JS_HAS_DEBUGGER_KEYWORD */ #if JS_HAS_XML_SUPPORT case JSOP_STARTXML: case JSOP_STARTXMLEXPR: inXML = op == JSOP_STARTXML; todo = -2; break; case JSOP_DEFXMLNS: rval = POP_STR(); js_printf(jp, "\t%s %s %s = %s;\n", js_default_str, js_xml_str, js_namespace_str, rval); todo = -2; break; case JSOP_ANYNAME: if (pc[JSOP_ANYNAME_LENGTH] == JSOP_TOATTRNAME) { len += JSOP_TOATTRNAME_LENGTH; todo = SprintPut(&ss->sprinter, "@*", 2); } else { todo = SprintPut(&ss->sprinter, "*", 1); } break; BEGIN_LITOPX_CASE(JSOP_QNAMEPART) if (pc[JSOP_QNAMEPART_LENGTH] == JSOP_TOATTRNAME) { saveop = JSOP_TOATTRNAME; len += JSOP_TOATTRNAME_LENGTH; lval = "@"; goto do_qname; } goto do_name; END_LITOPX_CASE BEGIN_LITOPX_CASE(JSOP_QNAMECONST) rval = QuoteString(&ss->sprinter, ATOM_TO_STRING(atom), 0); if (!rval) return NULL; RETRACT(&ss->sprinter, rval); lval = POP_STR(); todo = Sprint(&ss->sprinter, "%s::%s", lval, rval); END_LITOPX_CASE case JSOP_QNAME: rval = POP_STR(); lval = POP_STR(); todo = Sprint(&ss->sprinter, "%s::[%s]", lval, rval); break; case JSOP_TOATTRNAME: op = JSOP_NOP; /* turn off parens */ rval = POP_STR(); todo = Sprint(&ss->sprinter, "@[%s]", rval); break; case JSOP_TOATTRVAL: todo = -2; break; case JSOP_ADDATTRNAME: rval = POP_STR(); lval = POP_STR(); todo = Sprint(&ss->sprinter, "%s %s", lval, rval); /* This gets reset by all XML tag expressions. */ quoteAttr = JS_TRUE; break; case JSOP_ADDATTRVAL: rval = POP_STR(); lval = POP_STR(); if (quoteAttr) todo = Sprint(&ss->sprinter, "%s=\"%s\"", lval, rval); else todo = Sprint(&ss->sprinter, "%s=%s", lval, rval); break; case JSOP_BINDXMLNAME: /* Leave the name stacked and push a dummy string. */ todo = Sprint(&ss->sprinter, ""); break; case JSOP_SETXMLNAME: /* Pop the r.h.s., the dummy string, and the name. */ rval = POP_STR(); (void) PopOff(ss, op); lval = POP_STR(); goto do_setlval; case JSOP_XMLELTEXPR: case JSOP_XMLTAGEXPR: todo = Sprint(&ss->sprinter, "{%s}", POP_STR()); inXML = JS_TRUE; /* If we're an attribute value, we shouldn't quote this. */ quoteAttr = JS_FALSE; break; case JSOP_TOXMLLIST: op = JSOP_NOP; /* turn off parens */ todo = Sprint(&ss->sprinter, "<>%s", POP_STR()); inXML = JS_FALSE; break; case JSOP_FOREACH: foreach = JS_TRUE; todo = -2; break; case JSOP_TOXML: inXML = JS_FALSE; /* FALL THROUGH */ case JSOP_XMLNAME: case JSOP_FILTER: /* Conversion and prefix ops do nothing in the decompiler. */ todo = -2; break; case JSOP_ENDFILTER: rval = POP_STR(); lval = POP_STR(); todo = Sprint(&ss->sprinter, "%s.(%s)", lval, rval); break; case JSOP_DESCENDANTS: rval = POP_STR(); lval = POP_STR(); todo = Sprint(&ss->sprinter, "%s..%s", lval, rval); break; BEGIN_LITOPX_CASE(JSOP_XMLOBJECT) todo = Sprint(&ss->sprinter, "", ATOM_TO_OBJECT(atom)); END_LITOPX_CASE BEGIN_LITOPX_CASE(JSOP_XMLCDATA) todo = SprintPut(&ss->sprinter, "sprinter, ATOM_TO_STRING(atom), 0)) return NULL; SprintPut(&ss->sprinter, "]]>", 3); END_LITOPX_CASE BEGIN_LITOPX_CASE(JSOP_XMLCOMMENT) todo = SprintPut(&ss->sprinter, "", 3); END_LITOPX_CASE BEGIN_LITOPX_CASE(JSOP_XMLPI) rval = JS_strdup(cx, POP_STR()); if (!rval) return NULL; todo = SprintPut(&ss->sprinter, "sprinter, ATOM_TO_STRING(atom), 0) && (*rval == '\0' || (SprintPut(&ss->sprinter, " ", 1) >= 0 && SprintCString(&ss->sprinter, rval))); JS_free(cx, (char *)rval); if (!ok) return NULL; SprintPut(&ss->sprinter, "?>", 2); END_LITOPX_CASE case JSOP_GETFUNNS: todo = SprintPut(&ss->sprinter, js_function_str, 8); break; #endif /* JS_HAS_XML_SUPPORT */ default: todo = -2; break; #undef BEGIN_LITOPX_CASE #undef END_LITOPX_CASE } } if (todo < 0) { /* -2 means "don't push", -1 means reported error. */ if (todo == -1) return NULL; } else { if (!PushOff(ss, todo, saveop)) return NULL; } pc += len; } /* * Undefine local macros. */ #undef inXML #undef DECOMPILE_CODE #undef POP_STR #undef LOCAL_ASSERT #undef ATOM_IS_IDENTIFIER #undef GET_QUOTE_AND_FMT #undef GET_ATOM_QUOTE_AND_FMT return pc; } static JSBool InitSprintStack(JSContext *cx, SprintStack *ss, JSPrinter *jp, uintN depth) { size_t offsetsz, opcodesz; void *space; INIT_SPRINTER(cx, &ss->sprinter, &cx->tempPool, PAREN_SLOP); /* Allocate the parallel (to avoid padding) offset and opcode stacks. */ offsetsz = depth * sizeof(ptrdiff_t); opcodesz = depth * sizeof(jsbytecode); JS_ARENA_ALLOCATE(space, &cx->tempPool, offsetsz + opcodesz); if (!space) return JS_FALSE; ss->offsets = (ptrdiff_t *) space; ss->opcodes = (jsbytecode *) ((char *)space + offsetsz); ss->top = ss->inArrayInit = 0; ss->printer = jp; return JS_TRUE; } JSBool js_DecompileCode(JSPrinter *jp, JSScript *script, jsbytecode *pc, uintN len, uintN pcdepth) { uintN depth, i; SprintStack ss; JSContext *cx; void *mark; JSBool ok; JSScript *oldscript; char *last; depth = script->depth; JS_ASSERT(pcdepth <= depth); /* Initialize a sprinter for use with the offset stack. */ cx = jp->sprinter.context; mark = JS_ARENA_MARK(&cx->tempPool); ok = InitSprintStack(cx, &ss, jp, depth); if (!ok) goto out; /* * If we are called from js_DecompileValueGenerator with a portion of * script's bytecode that starts with a non-zero model stack depth given * by pcdepth, attempt to initialize the missing string offsets in ss to * |spindex| negative indexes from fp->sp for the activation fp in which * the error arose. * * See js_DecompileValueGenerator for how its |spindex| parameter is used, * and see also GetOff, which makes use of the ss.offsets[i] < -1 that are * potentially stored below. */ ss.top = pcdepth; if (pcdepth != 0) { JSStackFrame *fp; ptrdiff_t top; for (fp = cx->fp; fp && !fp->script; fp = fp->down) continue; top = fp ? fp->sp - fp->spbase : 0; for (i = 0; i < pcdepth; i++) { ss.offsets[i] = -1; ss.opcodes[i] = JSOP_NOP; } if (fp && fp->pc == pc && (uintN)top == pcdepth) { for (i = 0; i < pcdepth; i++) { ptrdiff_t off; jsbytecode *genpc; off = (intN)i - (intN)depth; genpc = (jsbytecode *) fp->spbase[off]; if (JS_UPTRDIFF(genpc, script->code) < script->length) { ss.offsets[i] += (ptrdiff_t)i - top; ss.opcodes[i] = *genpc; } } } } /* Call recursive subroutine to do the hard work. */ oldscript = jp->script; jp->script = script; ok = Decompile(&ss, pc, len) != NULL; jp->script = oldscript; /* If the given code didn't empty the stack, do it now. */ if (ss.top) { do { last = OFF2STR(&ss.sprinter, PopOff(&ss, JSOP_POP)); } while (ss.top > pcdepth); js_printf(jp, "%s", last); } out: /* Free all temporary stuff allocated under this call. */ JS_ARENA_RELEASE(&cx->tempPool, mark); return ok; } JSBool js_DecompileScript(JSPrinter *jp, JSScript *script) { return js_DecompileCode(jp, script, script->code, (uintN)script->length, 0); } static const char native_code_str[] = "\t[native code]\n"; JSBool js_DecompileFunctionBody(JSPrinter *jp, JSFunction *fun) { JSScript *script; JSScope *scope, *save; JSBool ok; if (!FUN_INTERPRETED(fun)) { js_printf(jp, native_code_str); return JS_TRUE; } script = fun->u.i.script; scope = fun->object ? OBJ_SCOPE(fun->object) : NULL; save = jp->scope; jp->scope = scope; ok = js_DecompileCode(jp, script, script->code, (uintN)script->length, 0); jp->scope = save; return ok; } JSBool js_DecompileFunction(JSPrinter *jp, JSFunction *fun) { JSContext *cx; uintN i, nargs, indent; void *mark; JSAtom **params; JSScope *scope, *oldscope; JSScopeProperty *sprop; jsbytecode *pc, *endpc; ptrdiff_t len; JSBool ok; /* * If pretty, conform to ECMA-262 Edition 3, 15.3.4.2, by decompiling a * FunctionDeclaration. Otherwise, check the JSFUN_LAMBDA flag and force * an expression by parenthesizing. */ if (jp->pretty) { js_printf(jp, "\t"); } else { if (!jp->grouped && (fun->flags & JSFUN_LAMBDA)) js_puts(jp, "("); } if (JSFUN_GETTER_TEST(fun->flags)) js_printf(jp, "%s ", js_getter_str); else if (JSFUN_SETTER_TEST(fun->flags)) js_printf(jp, "%s ", js_setter_str); js_printf(jp, "%s ", js_function_str); if (fun->atom && !QuoteString(&jp->sprinter, ATOM_TO_STRING(fun->atom), 0)) return JS_FALSE; js_puts(jp, "("); if (FUN_INTERPRETED(fun) && fun->object) { size_t paramsize; #ifdef JS_HAS_DESTRUCTURING SprintStack ss; JSScript *oldscript; #endif /* * Print the parameters. * * This code is complicated by the need to handle duplicate parameter * names, as required by ECMA (bah!). A duplicate parameter is stored * as another node with the same id (the parameter name) but different * shortid (the argument index) along the property tree ancestor line * starting at SCOPE_LAST_PROP(scope). Only the last duplicate param * is mapped by the scope's hash table. */ cx = jp->sprinter.context; nargs = fun->nargs; mark = JS_ARENA_MARK(&cx->tempPool); paramsize = nargs * sizeof(JSAtom *); JS_ARENA_ALLOCATE_CAST(params, JSAtom **, &cx->tempPool, paramsize); if (!params) { JS_ReportOutOfMemory(cx); return JS_FALSE; } memset(params, 0, paramsize); scope = OBJ_SCOPE(fun->object); for (sprop = SCOPE_LAST_PROP(scope); sprop; sprop = sprop->parent) { if (sprop->getter != js_GetArgument) continue; JS_ASSERT(sprop->flags & SPROP_HAS_SHORTID); JS_ASSERT((uint16) sprop->shortid < nargs); JS_ASSERT(JSID_IS_ATOM(sprop->id)); params[(uint16) sprop->shortid] = JSID_TO_ATOM(sprop->id); } pc = fun->u.i.script->main; endpc = pc + fun->u.i.script->length; ok = JS_TRUE; #ifdef JS_HAS_DESTRUCTURING /* Skip JSOP_GENERATOR in case of destructuring parameters. */ if (*pc == JSOP_GENERATOR) pc += JSOP_GENERATOR_LENGTH; ss.printer = NULL; oldscript = jp->script; jp->script = fun->u.i.script; oldscope = jp->scope; jp->scope = scope; #endif for (i = 0; i < nargs; i++) { if (i > 0) js_puts(jp, ", "); #if JS_HAS_DESTRUCTURING #define LOCAL_ASSERT(expr) LOCAL_ASSERT_RV(expr, JS_FALSE) if (!params[i]) { ptrdiff_t todo; const char *lval; LOCAL_ASSERT(*pc == JSOP_GETARG); pc += JSOP_GETARG_LENGTH; LOCAL_ASSERT(*pc == JSOP_DUP); if (!ss.printer) { ok = InitSprintStack(cx, &ss, jp, fun->u.i.script->depth); if (!ok) break; } pc = DecompileDestructuring(&ss, pc, endpc); if (!pc) { ok = JS_FALSE; break; } LOCAL_ASSERT(*pc == JSOP_POP); pc += JSOP_POP_LENGTH; lval = PopStr(&ss, JSOP_NOP); todo = SprintCString(&jp->sprinter, lval); if (todo < 0) { ok = JS_FALSE; break; } continue; } #undef LOCAL_ASSERT #endif if (!QuoteString(&jp->sprinter, ATOM_TO_STRING(params[i]), 0)) { ok = JS_FALSE; break; } } #ifdef JS_HAS_DESTRUCTURING jp->script = oldscript; jp->scope = oldscope; #endif JS_ARENA_RELEASE(&cx->tempPool, mark); if (!ok) return JS_FALSE; #ifdef __GNUC__ } else { scope = NULL; pc = NULL; #endif } js_printf(jp, ") {\n"); indent = jp->indent; jp->indent += 4; if (FUN_INTERPRETED(fun) && fun->object) { oldscope = jp->scope; jp->scope = scope; len = fun->u.i.script->code + fun->u.i.script->length - pc; ok = js_DecompileCode(jp, fun->u.i.script, pc, (uintN)len, 0); jp->scope = oldscope; if (!ok) { jp->indent = indent; return JS_FALSE; } } else { js_printf(jp, native_code_str); } jp->indent -= 4; js_printf(jp, "\t}"); if (!jp->pretty) { if (!jp->grouped && (fun->flags & JSFUN_LAMBDA)) js_puts(jp, ")"); } return JS_TRUE; } #undef LOCAL_ASSERT_RV JSString * js_DecompileValueGenerator(JSContext *cx, intN spindex, jsval v, JSString *fallback) { JSStackFrame *fp, *down; jsbytecode *pc, *begin, *end; jsval *sp, *spbase, *base, *limit; intN depth, pcdepth; JSScript *script; JSOp op; const JSCodeSpec *cs; jssrcnote *sn; ptrdiff_t len, oplen; JSPrinter *jp; JSString *name; for (fp = cx->fp; fp && !fp->script; fp = fp->down) continue; if (!fp) goto do_fallback; /* Try to find sp's generating pc depth slots under it on the stack. */ pc = fp->pc; sp = fp->sp; spbase = fp->spbase; if ((uintN)(sp - spbase) > fp->script->depth) { /* * Preparing to make an internal invocation, using an argv stack * segment pushed just above fp's operand stack space. Such an argv * stack has no generating pc "basement", so we must fall back. */ goto do_fallback; } if (spindex == JSDVG_SEARCH_STACK) { if (!pc) { /* * Current frame is native: look under it for a scripted call * in which a decompilable bytecode string that generated the * value as an actual argument might exist. */ JS_ASSERT(!fp->script && !(fp->fun && FUN_INTERPRETED(fp->fun))); down = fp->down; if (!down) goto do_fallback; script = down->script; spbase = down->spbase; base = fp->argv; limit = base + fp->argc; } else { /* * This should be a script activation, either a top-level * script or a scripted function. But be paranoid about calls * to js_DecompileValueGenerator from code that hasn't fully * initialized a (default-all-zeroes) frame. */ script = fp->script; spbase = base = fp->spbase; limit = fp->sp; } /* * Pure paranoia about default-zeroed frames being active while * js_DecompileValueGenerator is called. It can't hurt much now; * error reporting performance is not an issue. */ if (!script || !base || !limit) goto do_fallback; /* * Try to find operand-generating pc depth slots below sp. * * In the native case, we know the arguments have generating pc's * under them, on account of fp->down->script being non-null: all * compiled scripts get depth slots for generating pc's allocated * upon activation, at the top of js_Interpret. * * In the script or scripted function case, the same reasoning * applies to fp rather than to fp->down. * * We search from limit to base to find the most recently calculated * value matching v under assumption that it is it that caused * exception, see bug 328664. */ for (sp = limit;;) { if (sp <= base) goto do_fallback; --sp; if (*sp == v) { depth = (intN)script->depth; sp -= depth; pc = (jsbytecode *) *sp; break; } } } else { /* * At this point, pc may or may not be null, i.e., we could be in * a script activation, or we could be in a native frame that was * called by another native function. Check pc and script. */ if (!pc) goto do_fallback; script = fp->script; if (!script) goto do_fallback; if (spindex != JSDVG_IGNORE_STACK) { JS_ASSERT(spindex < 0); depth = (intN)script->depth; #if !JS_HAS_NO_SUCH_METHOD JS_ASSERT(-depth <= spindex); #endif spindex -= depth; base = (jsval *) cx->stackPool.current->base; limit = (jsval *) cx->stackPool.current->avail; sp = fp->sp + spindex; if (JS_UPTRDIFF(sp, base) < JS_UPTRDIFF(limit, base)) pc = (jsbytecode *) *sp; } } /* * Again, be paranoid, this time about possibly loading an invalid pc * from fp->sp[-(1+depth)]. */ if (JS_UPTRDIFF(pc, script->code) >= (jsuword)script->length) { pc = fp->pc; if (!pc) goto do_fallback; } op = (JSOp) *pc; if (op == JSOP_TRAP) op = JS_GetTrapOpcode(cx, script, pc); /* None of these stack-writing ops generates novel values. */ JS_ASSERT(op != JSOP_CASE && op != JSOP_CASEX && op != JSOP_DUP && op != JSOP_DUP2 && op != JSOP_SWAP); /* * |this| could convert to a very long object initialiser, so cite it by * its keyword name instead. */ if (op == JSOP_THIS) return JS_NewStringCopyZ(cx, js_this_str); /* * JSOP_BINDNAME is special: it generates a value, the base object of a * reference. But if it is the generating op for a diagnostic produced by * js_DecompileValueGenerator, the name being bound is irrelevant. Just * fall back to the base object. */ if (op == JSOP_BINDNAME) goto do_fallback; /* NAME ops are self-contained, others require left or right context. */ cs = &js_CodeSpec[op]; begin = pc; end = pc + cs->length; if ((cs->format & JOF_MODEMASK) != JOF_NAME) { JSSrcNoteType noteType; sn = js_GetSrcNote(script, pc); if (!sn) goto do_fallback; noteType = SN_TYPE(sn); if (noteType == SRC_PCBASE) { begin -= js_GetSrcNoteOffset(sn, 0); } else if (noteType == SRC_PCDELTA) { end = begin + js_GetSrcNoteOffset(sn, 0); begin += cs->length; } else { goto do_fallback; } } len = PTRDIFF(end, begin, jsbytecode); if (len <= 0) goto do_fallback; /* * Walk forward from script->main and compute starting stack depth. * FIXME: Code to compute oplen copied from js_Disassemble1 and reduced. * FIXME: Optimize to use last empty-stack sequence point. */ pcdepth = 0; for (pc = script->main; pc < begin; pc += oplen) { jsbytecode *pc2; uint32 type; intN nuses, ndefs; /* Let pc2 be non-null only for JSOP_LITOPX. */ pc2 = NULL; op = (JSOp) *pc; if (op == JSOP_TRAP) op = JS_GetTrapOpcode(cx, script, pc); cs = &js_CodeSpec[op]; oplen = cs->length; if (op == JSOP_SETSP) { pcdepth = GET_UINT16(pc); continue; } /* * A (C ? T : E) expression requires skipping either T (if begin is in * E) or both T and E (if begin is after the whole expression) before * adjusting pcdepth based on the JSOP_IFEQ or JSOP_IFEQX at pc that * tests condition C. We know that the stack depth can't change from * what it was with C on top of stack. */ sn = js_GetSrcNote(script, pc); if (sn && SN_TYPE(sn) == SRC_COND) { ptrdiff_t jmpoff, jmplen; jmpoff = js_GetSrcNoteOffset(sn, 0); if (pc + jmpoff < begin) { pc += jmpoff; op = *pc; JS_ASSERT(op == JSOP_GOTO || op == JSOP_GOTOX); cs = &js_CodeSpec[op]; oplen = cs->length; jmplen = GetJumpOffset(pc, pc); if (pc + jmplen < begin) { oplen = (uintN) jmplen; continue; } /* * Ok, begin lies in E. Manually pop C off the model stack, * since we have moved beyond the IFEQ now. */ --pcdepth; } } type = cs->format & JOF_TYPEMASK; switch (type) { case JOF_TABLESWITCH: case JOF_TABLESWITCHX: { jsint jmplen, i, low, high; jmplen = (type == JOF_TABLESWITCH) ? JUMP_OFFSET_LEN : JUMPX_OFFSET_LEN; pc2 = pc; pc2 += jmplen; low = GET_JUMP_OFFSET(pc2); pc2 += JUMP_OFFSET_LEN; high = GET_JUMP_OFFSET(pc2); pc2 += JUMP_OFFSET_LEN; for (i = low; i <= high; i++) pc2 += jmplen; oplen = 1 + pc2 - pc; break; } case JOF_LOOKUPSWITCH: case JOF_LOOKUPSWITCHX: { jsint jmplen; jsbytecode *pc2; jsatomid npairs; jmplen = (type == JOF_LOOKUPSWITCH) ? JUMP_OFFSET_LEN : JUMPX_OFFSET_LEN; pc2 = pc; pc2 += jmplen; npairs = GET_ATOM_INDEX(pc2); pc2 += ATOM_INDEX_LEN; while (npairs) { pc2 += ATOM_INDEX_LEN; pc2 += jmplen; npairs--; } oplen = 1 + pc2 - pc; break; } case JOF_LITOPX: pc2 = pc + 1 + LITERAL_INDEX_LEN; op = *pc2; cs = &js_CodeSpec[op]; JS_ASSERT(cs->length > ATOM_INDEX_LEN); oplen += cs->length - (1 + ATOM_INDEX_LEN); break; default:; } if (sn && SN_TYPE(sn) == SRC_HIDDEN) continue; nuses = cs->nuses; if (nuses < 0) { /* Call opcode pushes [callee, this, argv...]. */ nuses = 2 + GET_ARGC(pc); } else if (op == JSOP_RETSUB) { /* Pop [exception or hole, retsub pc-index]. */ JS_ASSERT(nuses == 0); nuses = 2; } else if (op == JSOP_LEAVEBLOCK || op == JSOP_LEAVEBLOCKEXPR) { JS_ASSERT(nuses == 0); nuses = GET_UINT16(pc); } pcdepth -= nuses; JS_ASSERT(pcdepth >= 0); ndefs = cs->ndefs; if (op == JSOP_FINALLY) { /* Push [exception or hole, retsub pc-index]. */ JS_ASSERT(ndefs == 0); ndefs = 2; } else if (op == JSOP_ENTERBLOCK) { jsatomid atomIndex; JSAtom *atom; JSObject *obj; JS_ASSERT(ndefs == 0); atomIndex = pc2 ? GET_LITERAL_INDEX(pc) : GET_ATOM_INDEX(pc); atom = js_GetAtom(cx, &script->atomMap, atomIndex); obj = ATOM_TO_OBJECT(atom); JS_ASSERT(OBJ_BLOCK_DEPTH(cx, obj) == pcdepth); ndefs = OBJ_BLOCK_COUNT(cx, obj); } pcdepth += ndefs; } name = NULL; jp = js_NewPrinter(cx, "js_DecompileValueGenerator", 0, JS_FALSE); if (jp) { if (fp->fun && fp->fun->object) { JS_ASSERT(OBJ_IS_NATIVE(fp->fun->object)); jp->scope = OBJ_SCOPE(fp->fun->object); } jp->dvgfence = end; if (js_DecompileCode(jp, script, begin, (uintN)len, (uintN)pcdepth)) name = js_GetPrinterOutput(jp); js_DestroyPrinter(jp); } return name; do_fallback: return fallback ? fallback : js_ValueToSource(cx, v); } pacparser-1.4.5/src/spidermonkey/js/src/jsopcode.h000066400000000000000000000325111464010763600221740ustar00rootroot00000000000000/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- * * ***** BEGIN LICENSE BLOCK ***** * Version: MPL 1.1/GPL 2.0/LGPL 2.1 * * The contents of this file are subject to the Mozilla Public License Version * 1.1 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * http://www.mozilla.org/MPL/ * * Software distributed under the License is distributed on an "AS IS" basis, * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License * for the specific language governing rights and limitations under the * License. * * The Original Code is Mozilla Communicator client code, released * March 31, 1998. * * The Initial Developer of the Original Code is * Netscape Communications Corporation. * Portions created by the Initial Developer are Copyright (C) 1998 * the Initial Developer. All Rights Reserved. * * Contributor(s): * * Alternatively, the contents of this file may be used under the terms of * either of the GNU General Public License Version 2 or later (the "GPL"), * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), * in which case the provisions of the GPL or the LGPL are applicable instead * of those above. If you wish to allow use of your version of this file only * under the terms of either the GPL or the LGPL, and not to allow others to * use your version of this file under the terms of the MPL, indicate your * decision by deleting the provisions above and replace them with the notice * and other provisions required by the GPL or the LGPL. If you do not delete * the provisions above, a recipient may use your version of this file under * the terms of any one of the MPL, the GPL or the LGPL. * * ***** END LICENSE BLOCK ***** */ #ifndef jsopcode_h___ #define jsopcode_h___ /* * JS bytecode definitions. */ #include #include "jsprvtd.h" #include "jspubtd.h" #include "jsutil.h" JS_BEGIN_EXTERN_C /* * JS operation bytecodes. */ typedef enum JSOp { #define OPDEF(op,val,name,token,length,nuses,ndefs,prec,format) \ op = val, #include "jsopcode.tbl" #undef OPDEF JSOP_LIMIT } JSOp; typedef enum JSOpLength { #define OPDEF(op,val,name,token,length,nuses,ndefs,prec,format) \ op##_LENGTH = length, #include "jsopcode.tbl" #undef OPDEF JSOP_LIMIT_LENGTH } JSOpLength; /* * JS bytecode formats. */ #define JOF_BYTE 0 /* single bytecode, no immediates */ #define JOF_JUMP 1 /* signed 16-bit jump offset immediate */ #define JOF_CONST 2 /* unsigned 16-bit constant pool index */ #define JOF_UINT16 3 /* unsigned 16-bit immediate operand */ #define JOF_TABLESWITCH 4 /* table switch */ #define JOF_LOOKUPSWITCH 5 /* lookup switch */ #define JOF_QARG 6 /* quickened get/set function argument ops */ #define JOF_QVAR 7 /* quickened get/set local variable ops */ #define JOF_INDEXCONST 8 /* uint16 slot index + constant pool index */ #define JOF_JUMPX 9 /* signed 32-bit jump offset immediate */ #define JOF_TABLESWITCHX 10 /* extended (32-bit offset) table switch */ #define JOF_LOOKUPSWITCHX 11 /* extended (32-bit offset) lookup switch */ #define JOF_UINT24 12 /* extended unsigned 24-bit literal (index) */ #define JOF_LITOPX 13 /* JOF_UINT24 followed by op being extended, where op if JOF_CONST has no unsigned 16- bit immediate operand */ #define JOF_LOCAL 14 /* block-local operand stack variable */ #define JOF_TYPEMASK 0x000f /* mask for above immediate types */ #define JOF_NAME 0x0010 /* name operation */ #define JOF_PROP 0x0020 /* obj.prop operation */ #define JOF_ELEM 0x0030 /* obj[index] operation */ #define JOF_MODEMASK 0x0030 /* mask for above addressing modes */ #define JOF_SET 0x0040 /* set (i.e., assignment) operation */ #define JOF_DEL 0x0080 /* delete operation */ #define JOF_DEC 0x0100 /* decrement (--, not ++) opcode */ #define JOF_INC 0x0200 /* increment (++, not --) opcode */ #define JOF_INCDEC 0x0300 /* increment or decrement opcode */ #define JOF_POST 0x0400 /* postorder increment or decrement */ #define JOF_IMPORT 0x0800 /* import property op */ #define JOF_FOR 0x1000 /* for-in property op */ #define JOF_ASSIGNING JOF_SET /* hint for JSClass.resolve, used for ops that do simplex assignment */ #define JOF_DETECTING 0x2000 /* object detection flag for JSNewResolveOp */ #define JOF_BACKPATCH 0x4000 /* backpatch placeholder during codegen */ #define JOF_LEFTASSOC 0x8000 /* left-associative operator */ #define JOF_DECLARING 0x10000 /* var, const, or function declaration op */ #define JOF_XMLNAME 0x20000 /* XML name: *, a::b, @a, @a::b, etc. */ #define JOF_TYPE_IS_EXTENDED_JUMP(t) \ ((unsigned)((t) - JOF_JUMPX) <= (unsigned)(JOF_LOOKUPSWITCHX - JOF_JUMPX)) /* * Immediate operand getters, setters, and bounds. */ /* Short (2-byte signed offset) relative jump macros. */ #define JUMP_OFFSET_LEN 2 #define JUMP_OFFSET_HI(off) ((jsbytecode)((off) >> 8)) #define JUMP_OFFSET_LO(off) ((jsbytecode)(off)) #define GET_JUMP_OFFSET(pc) ((int16)(((pc)[1] << 8) | (pc)[2])) #define SET_JUMP_OFFSET(pc,off) ((pc)[1] = JUMP_OFFSET_HI(off), \ (pc)[2] = JUMP_OFFSET_LO(off)) #define JUMP_OFFSET_MIN ((int16)0x8000) #define JUMP_OFFSET_MAX ((int16)0x7fff) /* * When a short jump won't hold a relative offset, its 2-byte immediate offset * operand is an unsigned index of a span-dependency record, maintained until * code generation finishes -- after which some (but we hope not nearly all) * span-dependent jumps must be extended (see OptimizeSpanDeps in jsemit.c). * * If the span-dependency record index overflows SPANDEP_INDEX_MAX, the jump * offset will contain SPANDEP_INDEX_HUGE, indicating that the record must be * found (via binary search) by its "before span-dependency optimization" pc * offset (from script main entry point). */ #define GET_SPANDEP_INDEX(pc) ((uint16)(((pc)[1] << 8) | (pc)[2])) #define SET_SPANDEP_INDEX(pc,i) ((pc)[1] = JUMP_OFFSET_HI(i), \ (pc)[2] = JUMP_OFFSET_LO(i)) #define SPANDEP_INDEX_MAX ((uint16)0xfffe) #define SPANDEP_INDEX_HUGE ((uint16)0xffff) /* Ultimately, if short jumps won't do, emit long (4-byte signed) offsets. */ #define JUMPX_OFFSET_LEN 4 #define JUMPX_OFFSET_B3(off) ((jsbytecode)((off) >> 24)) #define JUMPX_OFFSET_B2(off) ((jsbytecode)((off) >> 16)) #define JUMPX_OFFSET_B1(off) ((jsbytecode)((off) >> 8)) #define JUMPX_OFFSET_B0(off) ((jsbytecode)(off)) #define GET_JUMPX_OFFSET(pc) ((int32)(((pc)[1] << 24) | ((pc)[2] << 16) \ | ((pc)[3] << 8) | (pc)[4])) #define SET_JUMPX_OFFSET(pc,off)((pc)[1] = JUMPX_OFFSET_B3(off), \ (pc)[2] = JUMPX_OFFSET_B2(off), \ (pc)[3] = JUMPX_OFFSET_B1(off), \ (pc)[4] = JUMPX_OFFSET_B0(off)) #define JUMPX_OFFSET_MIN ((int32)0x80000000) #define JUMPX_OFFSET_MAX ((int32)0x7fffffff) /* * A literal is indexed by a per-script atom map. Most scripts have relatively * few literals, so the standard JOF_CONST format specifies a fixed 16 bits of * immediate operand index. A script with more than 64K literals must push all * high-indexed literals on the stack using JSOP_LITERAL, then use JOF_ELEM ops * instead of JOF_PROP, etc. */ #define ATOM_INDEX_LEN 2 #define ATOM_INDEX_HI(i) ((jsbytecode)((i) >> 8)) #define ATOM_INDEX_LO(i) ((jsbytecode)(i)) #define GET_ATOM_INDEX(pc) ((jsatomid)(((pc)[1] << 8) | (pc)[2])) #define SET_ATOM_INDEX(pc,i) ((pc)[1] = ATOM_INDEX_HI(i), \ (pc)[2] = ATOM_INDEX_LO(i)) #define GET_ATOM(cx,script,pc) js_GetAtom((cx), &(script)->atomMap, \ GET_ATOM_INDEX(pc)) /* A full atom index for JSOP_UINT24 uses 24 bits of immediate operand. */ #define UINT24_HI(i) ((jsbytecode)((i) >> 16)) #define UINT24_MID(i) ((jsbytecode)((i) >> 8)) #define UINT24_LO(i) ((jsbytecode)(i)) #define GET_UINT24(pc) ((jsatomid)(((pc)[1] << 16) | \ ((pc)[2] << 8) | \ (pc)[3])) #define SET_UINT24(pc,i) ((pc)[1] = UINT24_HI(i), \ (pc)[2] = UINT24_MID(i), \ (pc)[3] = UINT24_LO(i)) /* Same format for JSOP_LITERAL, etc., but future-proof with different names. */ #define LITERAL_INDEX_LEN 3 #define LITERAL_INDEX_HI(i) UINT24_HI(i) #define LITERAL_INDEX_MID(i) UINT24_MID(i) #define LITERAL_INDEX_LO(i) UINT24_LO(i) #define GET_LITERAL_INDEX(pc) GET_UINT24(pc) #define SET_LITERAL_INDEX(pc,i) SET_UINT24(pc,i) /* Atom index limit is determined by SN_3BYTE_OFFSET_FLAG, see jsemit.h. */ #define ATOM_INDEX_LIMIT_LOG2 23 #define ATOM_INDEX_LIMIT ((uint32)1 << ATOM_INDEX_LIMIT_LOG2) JS_STATIC_ASSERT(sizeof(jsatomid) * JS_BITS_PER_BYTE >= ATOM_INDEX_LIMIT_LOG2 + 1); /* Common uint16 immediate format helpers. */ #define UINT16_HI(i) ((jsbytecode)((i) >> 8)) #define UINT16_LO(i) ((jsbytecode)(i)) #define GET_UINT16(pc) ((uintN)(((pc)[1] << 8) | (pc)[2])) #define SET_UINT16(pc,i) ((pc)[1] = UINT16_HI(i), (pc)[2] = UINT16_LO(i)) #define UINT16_LIMIT ((uintN)1 << 16) /* Actual argument count operand format helpers. */ #define ARGC_HI(argc) UINT16_HI(argc) #define ARGC_LO(argc) UINT16_LO(argc) #define GET_ARGC(pc) GET_UINT16(pc) #define ARGC_LIMIT UINT16_LIMIT /* Synonyms for quick JOF_QARG and JOF_QVAR bytecodes. */ #define GET_ARGNO(pc) GET_UINT16(pc) #define SET_ARGNO(pc,argno) SET_UINT16(pc,argno) #define ARGNO_LEN 2 #define ARGNO_LIMIT UINT16_LIMIT #define GET_VARNO(pc) GET_UINT16(pc) #define SET_VARNO(pc,varno) SET_UINT16(pc,varno) #define VARNO_LEN 2 #define VARNO_LIMIT UINT16_LIMIT struct JSCodeSpec { const char *name; /* JS bytecode name */ const char *token; /* JS source literal or null */ int8 length; /* length including opcode byte */ int8 nuses; /* arity, -1 if variadic */ int8 ndefs; /* number of stack results */ uint8 prec; /* operator precedence */ uint32 format; /* immediate operand format */ }; extern const JSCodeSpec js_CodeSpec[]; extern uintN js_NumCodeSpecs; extern const jschar js_EscapeMap[]; /* * Return a GC'ed string containing the chars in str, with any non-printing * chars or quotes (' or " as specified by the quote argument) escaped, and * with the quote character at the beginning and end of the result string. */ extern JSString * js_QuoteString(JSContext *cx, JSString *str, jschar quote); /* * JSPrinter operations, for printf style message formatting. The return * value from js_GetPrinterOutput() is the printer's cumulative output, in * a GC'ed string. */ extern JSPrinter * js_NewPrinter(JSContext *cx, const char *name, uintN indent, JSBool pretty); extern void js_DestroyPrinter(JSPrinter *jp); extern JSString * js_GetPrinterOutput(JSPrinter *jp); extern int js_printf(JSPrinter *jp, const char *format, ...); extern JSBool js_puts(JSPrinter *jp, const char *s); #ifdef DEBUG /* * Disassemblers, for debugging only. */ #include extern JS_FRIEND_API(JSBool) js_Disassemble(JSContext *cx, JSScript *script, JSBool lines, FILE *fp); extern JS_FRIEND_API(uintN) js_Disassemble1(JSContext *cx, JSScript *script, jsbytecode *pc, uintN loc, JSBool lines, FILE *fp); #endif /* DEBUG */ /* * Decompilers, for script, function, and expression pretty-printing. */ extern JSBool js_DecompileCode(JSPrinter *jp, JSScript *script, jsbytecode *pc, uintN len, uintN pcdepth); extern JSBool js_DecompileScript(JSPrinter *jp, JSScript *script); extern JSBool js_DecompileFunctionBody(JSPrinter *jp, JSFunction *fun); extern JSBool js_DecompileFunction(JSPrinter *jp, JSFunction *fun); /* * Find the source expression that resulted in v, and return a new string * containing it. Fall back on v's string conversion (fallback) if we can't * find the bytecode that generated and pushed v on the operand stack. * * Search the current stack frame if spindex is JSDVG_SEARCH_STACK. Don't * look for v on the stack if spindex is JSDVG_IGNORE_STACK. Otherwise, * spindex is the negative index of v, measured from cx->fp->sp, or from a * lower frame's sp if cx->fp is native. */ extern JSString * js_DecompileValueGenerator(JSContext *cx, intN spindex, jsval v, JSString *fallback); #define JSDVG_IGNORE_STACK 0 #define JSDVG_SEARCH_STACK 1 JS_END_EXTERN_C #endif /* jsopcode_h___ */ pacparser-1.4.5/src/spidermonkey/js/src/jsopcode.tbl000066400000000000000000000700521464010763600225300ustar00rootroot00000000000000/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- * vim: set ts=8 sw=4 et tw=0 ft=C: * * ***** BEGIN LICENSE BLOCK ***** * Version: MPL 1.1/GPL 2.0/LGPL 2.1 * * The contents of this file are subject to the Mozilla Public License Version * 1.1 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * http://www.mozilla.org/MPL/ * * Software distributed under the License is distributed on an "AS IS" basis, * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License * for the specific language governing rights and limitations under the * License. * * The Original Code is Mozilla Communicator client code, released * March 31, 1998. * * The Initial Developer of the Original Code is * Netscape Communications Corporation. * Portions created by the Initial Developer are Copyright (C) 1998 * the Initial Developer. All Rights Reserved. * * Contributor(s): * * Alternatively, the contents of this file may be used under the terms of * either of the GNU General Public License Version 2 or later (the "GPL"), * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), * in which case the provisions of the GPL or the LGPL are applicable instead * of those above. If you wish to allow use of your version of this file only * under the terms of either the GPL or the LGPL, and not to allow others to * use your version of this file under the terms of the MPL, indicate your * decision by deleting the provisions above and replace them with the notice * and other provisions required by the GPL or the LGPL. If you do not delete * the provisions above, a recipient may use your version of this file under * the terms of any one of the MPL, the GPL or the LGPL. * * ***** END LICENSE BLOCK ***** */ /* * JavaScript operation bytecodes. If you need to allocate a bytecode, look * for a name of the form JSOP_UNUSED* and claim it. Otherwise, always add at * the end of the table. * * Includers must define an OPDEF macro of the following form: * * #define OPDEF(op,val,name,image,length,nuses,ndefs,prec,format) ... * * Selected arguments can be expanded in initializers. The op argument is * expanded followed by comma in the JSOp enum (jsopcode.h), e.g. The value * field must be dense for now, because jsopcode.c uses an OPDEF() expansion * inside the js_CodeSpec[] initializer. * * Field Description * op Bytecode name, which is the JSOp enumerator name * value Bytecode value, which is the JSOp enumerator value * name C string containing name for disassembler * image C string containing "image" for pretty-printer, null if ugly * length Number of bytes including any immediate operands * nuses Number of stack slots consumed by bytecode, -1 if variadic * ndefs Number of stack slots produced by bytecode * prec Operator precedence, zero if not an operator * format Bytecode plus immediate operand encoding format * * Precedence Operators Opcodes * 1 let (x = y) z, w JSOP_LEAVEBLOCKEXPR * 2 , JSOP_POP with SRC_PCDELTA note * 3 =, +=, etc. JSOP_SETNAME, etc. (all JOF_ASSIGNING) * 4 ?: JSOP_IFEQ, JSOP_IFEQX * 5 || JSOP_OR, JSOP_ORX * 6 && JSOP_AND, JSOP_ANDX * 7 | JSOP_BITOR * 8 ^ JSOP_BITXOR * 9 & JSOP_BITAND * 10 ==, !=, etc. JSOP_EQ, JSOP_NE, etc. * 11 <, in, etc. JSOP_LT, JSOP_IN, etc. * 12 <<, >>, >>> JSOP_LSH, JSOP_RSH, JSOP_URSH * 13 +, -, etc. JSOP_ADD, JSOP_SUB, etc. * 14 *, /, % JSOP_MUL, JSOP_DIV, JSOP_MOD * 15 !, ~, etc. JSOP_NOT, JSOP_BITNOT, etc. * 16 0, function(){} etc. JSOP_ZERO, JSOP_ANONFUNOBJ, etc. * 17 delete, new JSOP_DEL*, JSOP_NEW * 18 x.y, f(), etc. JSOP_GETPROP, JSOP_CALL, etc. * 19 x, null, etc. JSOP_NAME, JSOP_NULL, etc. * * The push-numeric-constant operators, JSOP_ZERO, JSOP_NUMBER, etc., have * lower precedence than the member operators emitted for the . operator, to * cause the decompiler to parenthesize the . left operand, e.g. (0).foo. * Otherwise the . could be taken as a decimal point. We use the same level * 16 for function expressions too, to force parenthesization. * * This file is best viewed with 128 columns: 12345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678 */ /* legend: op val name image len use def prec format */ /* Longstanding JavaScript bytecodes. */ OPDEF(JSOP_NOP, 0, "nop", NULL, 1, 0, 0, 0, JOF_BYTE) OPDEF(JSOP_PUSH, 1, "push", NULL, 1, 0, 1, 0, JOF_BYTE) OPDEF(JSOP_POPV, 2, "popv", NULL, 1, 1, 0, 2, JOF_BYTE) OPDEF(JSOP_ENTERWITH, 3, "enterwith", NULL, 1, 1, 1, 0, JOF_BYTE) OPDEF(JSOP_LEAVEWITH, 4, "leavewith", NULL, 1, 1, 0, 0, JOF_BYTE) OPDEF(JSOP_RETURN, 5, "return", NULL, 1, 1, 0, 0, JOF_BYTE) OPDEF(JSOP_GOTO, 6, "goto", NULL, 3, 0, 0, 0, JOF_JUMP) OPDEF(JSOP_IFEQ, 7, "ifeq", NULL, 3, 1, 0, 4, JOF_JUMP|JOF_DETECTING) OPDEF(JSOP_IFNE, 8, "ifne", NULL, 3, 1, 0, 0, JOF_JUMP) /* Get the arguments object for the current, lightweight function activation. */ OPDEF(JSOP_ARGUMENTS, 9, js_arguments_str, js_arguments_str, 1, 0, 1, 18, JOF_BYTE) /* ECMA-compliant for-in loop with argument or local variable loop control. */ OPDEF(JSOP_FORARG, 10, "forarg", NULL, 3, 0, 1, 19, JOF_QARG|JOF_NAME|JOF_FOR) OPDEF(JSOP_FORVAR, 11, "forvar", NULL, 3, 0, 1, 19, JOF_QVAR|JOF_NAME|JOF_FOR) /* More longstanding bytecodes. */ OPDEF(JSOP_DUP, 12, "dup", NULL, 1, 1, 2, 0, JOF_BYTE) OPDEF(JSOP_DUP2, 13, "dup2", NULL, 1, 2, 4, 0, JOF_BYTE) OPDEF(JSOP_SETCONST, 14, "setconst", NULL, 3, 1, 1, 3, JOF_CONST|JOF_NAME|JOF_SET|JOF_ASSIGNING) OPDEF(JSOP_BITOR, 15, "bitor", "|", 1, 2, 1, 7, JOF_BYTE|JOF_LEFTASSOC) OPDEF(JSOP_BITXOR, 16, "bitxor", "^", 1, 2, 1, 8, JOF_BYTE|JOF_LEFTASSOC) OPDEF(JSOP_BITAND, 17, "bitand", "&", 1, 2, 1, 9, JOF_BYTE|JOF_LEFTASSOC) OPDEF(JSOP_EQ, 18, "eq", "==", 1, 2, 1, 10, JOF_BYTE|JOF_LEFTASSOC|JOF_DETECTING) OPDEF(JSOP_NE, 19, "ne", "!=", 1, 2, 1, 10, JOF_BYTE|JOF_LEFTASSOC|JOF_DETECTING) OPDEF(JSOP_LT, 20, "lt", "<", 1, 2, 1, 11, JOF_BYTE|JOF_LEFTASSOC) OPDEF(JSOP_LE, 21, "le", "<=", 1, 2, 1, 11, JOF_BYTE|JOF_LEFTASSOC) OPDEF(JSOP_GT, 22, "gt", ">", 1, 2, 1, 11, JOF_BYTE|JOF_LEFTASSOC) OPDEF(JSOP_GE, 23, "ge", ">=", 1, 2, 1, 11, JOF_BYTE|JOF_LEFTASSOC) OPDEF(JSOP_LSH, 24, "lsh", "<<", 1, 2, 1, 12, JOF_BYTE|JOF_LEFTASSOC) OPDEF(JSOP_RSH, 25, "rsh", ">>", 1, 2, 1, 12, JOF_BYTE|JOF_LEFTASSOC) OPDEF(JSOP_URSH, 26, "ursh", ">>>", 1, 2, 1, 12, JOF_BYTE|JOF_LEFTASSOC) OPDEF(JSOP_ADD, 27, "add", "+", 1, 2, 1, 13, JOF_BYTE|JOF_LEFTASSOC) OPDEF(JSOP_SUB, 28, "sub", "-", 1, 2, 1, 13, JOF_BYTE|JOF_LEFTASSOC) OPDEF(JSOP_MUL, 29, "mul", "*", 1, 2, 1, 14, JOF_BYTE|JOF_LEFTASSOC) OPDEF(JSOP_DIV, 30, "div", "/", 1, 2, 1, 14, JOF_BYTE|JOF_LEFTASSOC) OPDEF(JSOP_MOD, 31, "mod", "%", 1, 2, 1, 14, JOF_BYTE|JOF_LEFTASSOC) OPDEF(JSOP_NOT, 32, "not", "!", 1, 1, 1, 15, JOF_BYTE|JOF_DETECTING) OPDEF(JSOP_BITNOT, 33, "bitnot", "~", 1, 1, 1, 15, JOF_BYTE) OPDEF(JSOP_NEG, 34, "neg", "- ", 1, 1, 1, 15, JOF_BYTE) OPDEF(JSOP_NEW, 35, js_new_str, NULL, 3, -1, 1, 17, JOF_UINT16) OPDEF(JSOP_DELNAME, 36, "delname", NULL, 3, 0, 1, 17, JOF_CONST|JOF_NAME|JOF_DEL) OPDEF(JSOP_DELPROP, 37, "delprop", NULL, 3, 1, 1, 17, JOF_CONST|JOF_PROP|JOF_DEL) OPDEF(JSOP_DELELEM, 38, "delelem", NULL, 1, 2, 1, 17, JOF_BYTE |JOF_ELEM|JOF_DEL) OPDEF(JSOP_TYPEOF, 39, js_typeof_str,NULL, 1, 1, 1, 15, JOF_BYTE|JOF_DETECTING) OPDEF(JSOP_VOID, 40, js_void_str, NULL, 1, 1, 1, 15, JOF_BYTE) OPDEF(JSOP_INCNAME, 41, "incname", NULL, 3, 0, 1, 15, JOF_CONST|JOF_NAME|JOF_INC) OPDEF(JSOP_INCPROP, 42, "incprop", NULL, 3, 1, 1, 15, JOF_CONST|JOF_PROP|JOF_INC) OPDEF(JSOP_INCELEM, 43, "incelem", NULL, 1, 2, 1, 15, JOF_BYTE |JOF_ELEM|JOF_INC) OPDEF(JSOP_DECNAME, 44, "decname", NULL, 3, 0, 1, 15, JOF_CONST|JOF_NAME|JOF_DEC) OPDEF(JSOP_DECPROP, 45, "decprop", NULL, 3, 1, 1, 15, JOF_CONST|JOF_PROP|JOF_DEC) OPDEF(JSOP_DECELEM, 46, "decelem", NULL, 1, 2, 1, 15, JOF_BYTE |JOF_ELEM|JOF_DEC) OPDEF(JSOP_NAMEINC, 47, "nameinc", NULL, 3, 0, 1, 15, JOF_CONST|JOF_NAME|JOF_INC|JOF_POST) OPDEF(JSOP_PROPINC, 48, "propinc", NULL, 3, 1, 1, 15, JOF_CONST|JOF_PROP|JOF_INC|JOF_POST) OPDEF(JSOP_ELEMINC, 49, "eleminc", NULL, 1, 2, 1, 15, JOF_BYTE |JOF_ELEM|JOF_INC|JOF_POST) OPDEF(JSOP_NAMEDEC, 50, "namedec", NULL, 3, 0, 1, 15, JOF_CONST|JOF_NAME|JOF_DEC|JOF_POST) OPDEF(JSOP_PROPDEC, 51, "propdec", NULL, 3, 1, 1, 15, JOF_CONST|JOF_PROP|JOF_DEC|JOF_POST) OPDEF(JSOP_ELEMDEC, 52, "elemdec", NULL, 1, 2, 1, 15, JOF_BYTE |JOF_ELEM|JOF_DEC|JOF_POST) OPDEF(JSOP_GETPROP, 53, "getprop", NULL, 3, 1, 1, 18, JOF_CONST|JOF_PROP) OPDEF(JSOP_SETPROP, 54, "setprop", NULL, 3, 2, 1, 3, JOF_CONST|JOF_PROP|JOF_SET|JOF_ASSIGNING|JOF_DETECTING) OPDEF(JSOP_GETELEM, 55, "getelem", NULL, 1, 2, 1, 18, JOF_BYTE |JOF_ELEM|JOF_LEFTASSOC) OPDEF(JSOP_SETELEM, 56, "setelem", NULL, 1, 3, 1, 3, JOF_BYTE |JOF_ELEM|JOF_SET|JOF_ASSIGNING|JOF_DETECTING) OPDEF(JSOP_PUSHOBJ, 57, "pushobj", NULL, 1, 0, 1, 0, JOF_BYTE) OPDEF(JSOP_CALL, 58, "call", NULL, 3, -1, 1, 18, JOF_UINT16) OPDEF(JSOP_NAME, 59, "name", NULL, 3, 0, 1, 19, JOF_CONST|JOF_NAME) OPDEF(JSOP_NUMBER, 60, "number", NULL, 3, 0, 1, 16, JOF_CONST) OPDEF(JSOP_STRING, 61, "string", NULL, 3, 0, 1, 19, JOF_CONST) OPDEF(JSOP_ZERO, 62, "zero", "0", 1, 0, 1, 16, JOF_BYTE) OPDEF(JSOP_ONE, 63, "one", "1", 1, 0, 1, 16, JOF_BYTE) OPDEF(JSOP_NULL, 64, js_null_str, js_null_str, 1, 0, 1, 19, JOF_BYTE) OPDEF(JSOP_THIS, 65, js_this_str, js_this_str, 1, 0, 1, 19, JOF_BYTE) OPDEF(JSOP_FALSE, 66, js_false_str, js_false_str, 1, 0, 1, 19, JOF_BYTE) OPDEF(JSOP_TRUE, 67, js_true_str, js_true_str, 1, 0, 1, 19, JOF_BYTE) OPDEF(JSOP_OR, 68, "or", NULL, 3, 1, 0, 5, JOF_JUMP|JOF_DETECTING) OPDEF(JSOP_AND, 69, "and", NULL, 3, 1, 0, 6, JOF_JUMP|JOF_DETECTING) /* The switch bytecodes have variable length. */ OPDEF(JSOP_TABLESWITCH, 70, "tableswitch", NULL, -1, 1, 0, 0, JOF_TABLESWITCH|JOF_DETECTING) OPDEF(JSOP_LOOKUPSWITCH, 71, "lookupswitch", NULL, -1, 1, 0, 0, JOF_LOOKUPSWITCH|JOF_DETECTING) /* New, infallible/transitive identity ops. */ OPDEF(JSOP_NEW_EQ, 72, "eq", NULL, 1, 2, 1, 10, JOF_BYTE|JOF_DETECTING) OPDEF(JSOP_NEW_NE, 73, "ne", NULL, 1, 2, 1, 10, JOF_BYTE|JOF_DETECTING) /* Lexical closure constructor. */ OPDEF(JSOP_CLOSURE, 74, "closure", NULL, 3, 0, 0, 0, JOF_CONST) /* Export and import ops. */ OPDEF(JSOP_EXPORTALL, 75, "exportall", NULL, 1, 0, 0, 0, JOF_BYTE) OPDEF(JSOP_EXPORTNAME,76, "exportname", NULL, 3, 0, 0, 0, JOF_CONST|JOF_NAME) OPDEF(JSOP_IMPORTALL, 77, "importall", NULL, 1, 1, 0, 0, JOF_BYTE) OPDEF(JSOP_IMPORTPROP,78, "importprop", NULL, 3, 1, 0, 0, JOF_CONST|JOF_PROP|JOF_IMPORT) OPDEF(JSOP_IMPORTELEM,79, "importelem", NULL, 1, 2, 0, 0, JOF_BYTE |JOF_ELEM|JOF_IMPORT) /* Push object literal. */ OPDEF(JSOP_OBJECT, 80, "object", NULL, 3, 0, 1, 19, JOF_CONST) /* Pop value and discard it. */ OPDEF(JSOP_POP, 81, "pop", NULL, 1, 1, 0, 2, JOF_BYTE) /* Convert value to number, for unary +. */ OPDEF(JSOP_POS, 82, "pos", "+ ", 1, 1, 1, 15, JOF_BYTE) /* Trap into debugger for breakpoint, etc. */ OPDEF(JSOP_TRAP, 83, "trap", NULL, 1, 0, 0, 0, JOF_BYTE) /* Fast get/set ops for function arguments and local variables. */ OPDEF(JSOP_GETARG, 84, "getarg", NULL, 3, 0, 1, 19, JOF_QARG |JOF_NAME) OPDEF(JSOP_SETARG, 85, "setarg", NULL, 3, 1, 1, 3, JOF_QARG |JOF_NAME|JOF_SET|JOF_ASSIGNING) OPDEF(JSOP_GETVAR, 86, "getvar", NULL, 3, 0, 1, 19, JOF_QVAR |JOF_NAME) OPDEF(JSOP_SETVAR, 87, "setvar", NULL, 3, 1, 1, 3, JOF_QVAR |JOF_NAME|JOF_SET|JOF_ASSIGNING|JOF_DETECTING) /* Push unsigned 16-bit int constant. */ OPDEF(JSOP_UINT16, 88, "uint16", NULL, 3, 0, 1, 16, JOF_UINT16) /* Object and array literal support. */ OPDEF(JSOP_NEWINIT, 89, "newinit", NULL, 1, 2, 1, 0, JOF_BYTE) OPDEF(JSOP_ENDINIT, 90, "endinit", NULL, 1, 0, 0, 19, JOF_BYTE) OPDEF(JSOP_INITPROP, 91, "initprop", NULL, 3, 1, 0, 3, JOF_CONST|JOF_PROP|JOF_DETECTING) OPDEF(JSOP_INITELEM, 92, "initelem", NULL, 1, 2, 0, 3, JOF_BYTE |JOF_ELEM|JOF_DETECTING) OPDEF(JSOP_DEFSHARP, 93, "defsharp", NULL, 3, 0, 0, 0, JOF_UINT16) OPDEF(JSOP_USESHARP, 94, "usesharp", NULL, 3, 0, 1, 0, JOF_UINT16) /* Fast inc/dec ops for args and local vars. */ OPDEF(JSOP_INCARG, 95, "incarg", NULL, 3, 0, 1, 15, JOF_QARG |JOF_NAME|JOF_INC) OPDEF(JSOP_INCVAR, 96, "incvar", NULL, 3, 0, 1, 15, JOF_QVAR |JOF_NAME|JOF_INC) OPDEF(JSOP_DECARG, 97, "decarg", NULL, 3, 0, 1, 15, JOF_QARG |JOF_NAME|JOF_DEC) OPDEF(JSOP_DECVAR, 98, "decvar", NULL, 3, 0, 1, 15, JOF_QVAR |JOF_NAME|JOF_DEC) OPDEF(JSOP_ARGINC, 99, "arginc", NULL, 3, 0, 1, 15, JOF_QARG |JOF_NAME|JOF_INC|JOF_POST) OPDEF(JSOP_VARINC, 100,"varinc", NULL, 3, 0, 1, 15, JOF_QVAR |JOF_NAME|JOF_INC|JOF_POST) OPDEF(JSOP_ARGDEC, 101,"argdec", NULL, 3, 0, 1, 15, JOF_QARG |JOF_NAME|JOF_DEC|JOF_POST) OPDEF(JSOP_VARDEC, 102,"vardec", NULL, 3, 0, 1, 15, JOF_QVAR |JOF_NAME|JOF_DEC|JOF_POST) /* * Initialize for-in iterator. See also JSOP_FOREACH and JSOP_FOREACHKEYVAL. */ OPDEF(JSOP_FORIN, 103,"forin", NULL, 1, 1, 1, 0, JOF_BYTE) /* ECMA-compliant for/in ops. */ OPDEF(JSOP_FORNAME, 104,"forname", NULL, 3, 0, 1, 19, JOF_CONST|JOF_NAME|JOF_FOR) OPDEF(JSOP_FORPROP, 105,"forprop", NULL, 3, 1, 1, 18, JOF_CONST|JOF_PROP|JOF_FOR) OPDEF(JSOP_FORELEM, 106,"forelem", NULL, 1, 1, 3, 18, JOF_BYTE |JOF_ELEM|JOF_FOR) OPDEF(JSOP_POP2, 107,"pop2", NULL, 1, 2, 0, 0, JOF_BYTE) /* ECMA-compliant assignment ops. */ OPDEF(JSOP_BINDNAME, 108,"bindname", NULL, 3, 0, 1, 0, JOF_CONST|JOF_NAME|JOF_SET|JOF_ASSIGNING) OPDEF(JSOP_SETNAME, 109,"setname", NULL, 3, 2, 1, 3, JOF_CONST|JOF_NAME|JOF_SET|JOF_ASSIGNING|JOF_DETECTING) /* Exception handling ops. */ OPDEF(JSOP_THROW, 110,"throw", NULL, 1, 1, 0, 0, JOF_BYTE) /* 'in' and 'instanceof' ops. */ OPDEF(JSOP_IN, 111,js_in_str, js_in_str, 1, 2, 1, 11, JOF_BYTE|JOF_LEFTASSOC) OPDEF(JSOP_INSTANCEOF,112,js_instanceof_str,js_instanceof_str,1,2,1,11,JOF_BYTE|JOF_LEFTASSOC) /* debugger op */ OPDEF(JSOP_DEBUGGER, 113,"debugger", NULL, 1, 0, 0, 0, JOF_BYTE) /* gosub/retsub for finally handling */ OPDEF(JSOP_GOSUB, 114,"gosub", NULL, 3, 0, 0, 0, JOF_JUMP) OPDEF(JSOP_RETSUB, 115,"retsub", NULL, 1, 0, 0, 0, JOF_BYTE) /* More exception handling ops. */ OPDEF(JSOP_EXCEPTION, 116,"exception", NULL, 1, 0, 1, 0, JOF_BYTE) OPDEF(JSOP_SETSP, 117,"setsp", NULL, 3, 0, 0, 0, JOF_UINT16) /* * ECMA-compliant switch statement ops. * CONDSWITCH is a decompilable NOP; CASE is ===, POP, jump if true, re-push * lval if false; and DEFAULT is POP lval and GOTO. */ OPDEF(JSOP_CONDSWITCH,118,"condswitch", NULL, 1, 0, 0, 0, JOF_BYTE) OPDEF(JSOP_CASE, 119,"case", NULL, 3, 1, 0, 0, JOF_JUMP) OPDEF(JSOP_DEFAULT, 120,"default", NULL, 3, 1, 0, 0, JOF_JUMP) /* * ECMA-compliant call to eval op */ OPDEF(JSOP_EVAL, 121,"eval", NULL, 3, -1, 1, 18, JOF_UINT16) /* * ECMA-compliant helper for 'for (x[i] in o)' loops. */ OPDEF(JSOP_ENUMELEM, 122,"enumelem", NULL, 1, 3, 0, 3, JOF_BYTE |JOF_SET|JOF_ASSIGNING) /* * Getter and setter prefix bytecodes. These modify the next bytecode, either * an assignment or a property initializer code, which then defines a property * getter or setter. */ OPDEF(JSOP_GETTER, 123,js_getter_str,NULL, 1, 0, 0, 0, JOF_BYTE) OPDEF(JSOP_SETTER, 124,js_setter_str,NULL, 1, 0, 0, 0, JOF_BYTE) /* * Prolog bytecodes for defining function, var, and const names. */ OPDEF(JSOP_DEFFUN, 125,"deffun", NULL, 3, 0, 0, 0, JOF_CONST|JOF_DECLARING) OPDEF(JSOP_DEFCONST, 126,"defconst", NULL, 3, 0, 0, 0, JOF_CONST|JOF_DECLARING) OPDEF(JSOP_DEFVAR, 127,"defvar", NULL, 3, 0, 0, 0, JOF_CONST|JOF_DECLARING) /* Auto-clone (if needed due to re-parenting) and push an anonymous function. */ OPDEF(JSOP_ANONFUNOBJ, 128, "anonfunobj", NULL, 3, 0, 1, 16, JOF_CONST) /* ECMA ed. 3 named function expression. */ OPDEF(JSOP_NAMEDFUNOBJ, 129, "namedfunobj", NULL, 3, 0, 1, 16, JOF_CONST) /* * Like JSOP_SETLOCAL, but specialized to avoid requiring JSOP_POP immediately * after to throw away the exception value. */ OPDEF(JSOP_SETLOCALPOP, 130, "setlocalpop", NULL, 3, 1, 0, 3, JOF_LOCAL|JOF_NAME|JOF_SET) /* ECMA-mandated parenthesization opcode, which nulls the reference base register, obj; see jsinterp.c. */ OPDEF(JSOP_GROUP, 131, "group", NULL, 1, 0, 0, 0, JOF_BYTE) /* Host object extension: given 'o.item(i) = j', the left-hand side compiles JSOP_SETCALL, rather than JSOP_CALL. */ OPDEF(JSOP_SETCALL, 132, "setcall", NULL, 3, -1, 2, 18, JOF_UINT16|JOF_SET|JOF_ASSIGNING) /* * Exception handling no-ops, for more economical byte-coding than SRC_TRYFIN * srcnote-annotated JSOP_NOPs. */ OPDEF(JSOP_TRY, 133,"try", NULL, 1, 0, 0, 0, JOF_BYTE) OPDEF(JSOP_FINALLY, 134,"finally", NULL, 1, 0, 0, 0, JOF_BYTE) /* * Swap the top two stack elements. */ OPDEF(JSOP_SWAP, 135,"swap", NULL, 1, 2, 2, 0, JOF_BYTE) /* * Bytecodes that avoid making an arguments object in most cases: * JSOP_ARGSUB gets arguments[i] from fp->argv, iff i is in [0, fp->argc-1]. * JSOP_ARGCNT returns fp->argc. */ OPDEF(JSOP_ARGSUB, 136,"argsub", NULL, 3, 0, 1, 18, JOF_QARG |JOF_NAME) OPDEF(JSOP_ARGCNT, 137,"argcnt", NULL, 1, 0, 1, 18, JOF_BYTE) /* * Define a local function object as a local variable. * The local variable's slot number is the first immediate two-byte operand. * The function object's atom index is the second immediate operand. */ OPDEF(JSOP_DEFLOCALFUN, 138,"deflocalfun",NULL, 5, 0, 0, 0, JOF_INDEXCONST|JOF_DECLARING) /* Extended jumps. */ OPDEF(JSOP_GOTOX, 139,"gotox", NULL, 5, 0, 0, 0, JOF_JUMPX) OPDEF(JSOP_IFEQX, 140,"ifeqx", NULL, 5, 1, 0, 3, JOF_JUMPX|JOF_DETECTING) OPDEF(JSOP_IFNEX, 141,"ifnex", NULL, 5, 1, 0, 0, JOF_JUMPX) OPDEF(JSOP_ORX, 142,"orx", NULL, 5, 1, 0, 5, JOF_JUMPX|JOF_DETECTING) OPDEF(JSOP_ANDX, 143,"andx", NULL, 5, 1, 0, 6, JOF_JUMPX|JOF_DETECTING) OPDEF(JSOP_GOSUBX, 144,"gosubx", NULL, 5, 0, 0, 0, JOF_JUMPX) OPDEF(JSOP_CASEX, 145,"casex", NULL, 5, 1, 0, 0, JOF_JUMPX) OPDEF(JSOP_DEFAULTX, 146,"defaultx", NULL, 5, 1, 0, 0, JOF_JUMPX) OPDEF(JSOP_TABLESWITCHX, 147,"tableswitchx",NULL, -1, 1, 0, 0, JOF_TABLESWITCHX|JOF_DETECTING) OPDEF(JSOP_LOOKUPSWITCHX, 148,"lookupswitchx",NULL, -1, 1, 0, 0, JOF_LOOKUPSWITCHX|JOF_DETECTING) /* Placeholders for a real jump opcode set during backpatch chain fixup. */ OPDEF(JSOP_BACKPATCH, 149,"backpatch",NULL, 3, 0, 0, 0, JOF_JUMP|JOF_BACKPATCH) OPDEF(JSOP_BACKPATCH_POP, 150,"backpatch_pop",NULL, 3, 1, 0, 0, JOF_JUMP|JOF_BACKPATCH) /* Set pending exception from the stack, to trigger rethrow. */ OPDEF(JSOP_THROWING, 151,"throwing", NULL, 1, 1, 0, 0, JOF_BYTE) /* Set and get return value pseudo-register in stack frame. */ OPDEF(JSOP_SETRVAL, 152,"setrval", NULL, 1, 1, 0, 0, JOF_BYTE) OPDEF(JSOP_RETRVAL, 153,"retrval", NULL, 1, 0, 0, 0, JOF_BYTE) /* Optimized global variable ops (we don't bother doing a JSOP_FORGVAR op). */ OPDEF(JSOP_GETGVAR, 154,"getgvar", NULL, 3, 0, 1, 19, JOF_CONST|JOF_NAME) OPDEF(JSOP_SETGVAR, 155,"setgvar", NULL, 3, 1, 1, 3, JOF_CONST|JOF_NAME|JOF_SET|JOF_ASSIGNING|JOF_DETECTING) OPDEF(JSOP_INCGVAR, 156,"incgvar", NULL, 3, 0, 1, 15, JOF_CONST|JOF_NAME|JOF_INC) OPDEF(JSOP_DECGVAR, 157,"decgvar", NULL, 3, 0, 1, 15, JOF_CONST|JOF_NAME|JOF_DEC) OPDEF(JSOP_GVARINC, 158,"gvarinc", NULL, 3, 0, 1, 15, JOF_CONST|JOF_NAME|JOF_INC|JOF_POST) OPDEF(JSOP_GVARDEC, 159,"gvardec", NULL, 3, 0, 1, 15, JOF_CONST|JOF_NAME|JOF_DEC|JOF_POST) /* Regular expression literal requiring special "fork on exec" handling. */ OPDEF(JSOP_REGEXP, 160,"regexp", NULL, 3, 0, 1, 19, JOF_CONST) /* XML (ECMA-357, a.k.a. "E4X") support. */ OPDEF(JSOP_DEFXMLNS, 161,"defxmlns", NULL, 1, 1, 0, 0, JOF_BYTE) OPDEF(JSOP_ANYNAME, 162,"anyname", NULL, 1, 0, 1, 19, JOF_BYTE|JOF_XMLNAME) OPDEF(JSOP_QNAMEPART, 163,"qnamepart", NULL, 3, 0, 1, 19, JOF_CONST|JOF_XMLNAME) OPDEF(JSOP_QNAMECONST, 164,"qnameconst", NULL, 3, 1, 1, 19, JOF_CONST|JOF_XMLNAME) OPDEF(JSOP_QNAME, 165,"qname", NULL, 1, 2, 1, 0, JOF_BYTE|JOF_XMLNAME) OPDEF(JSOP_TOATTRNAME, 166,"toattrname", NULL, 1, 1, 1, 19, JOF_BYTE|JOF_XMLNAME) OPDEF(JSOP_TOATTRVAL, 167,"toattrval", NULL, 1, 1, 1, 19, JOF_BYTE) OPDEF(JSOP_ADDATTRNAME, 168,"addattrname",NULL, 1, 2, 1, 13, JOF_BYTE) OPDEF(JSOP_ADDATTRVAL, 169,"addattrval", NULL, 1, 2, 1, 13, JOF_BYTE) OPDEF(JSOP_BINDXMLNAME, 170,"bindxmlname",NULL, 1, 1, 2, 3, JOF_BYTE|JOF_SET|JOF_ASSIGNING) OPDEF(JSOP_SETXMLNAME, 171,"setxmlname", NULL, 1, 3, 1, 3, JOF_BYTE|JOF_SET|JOF_ASSIGNING|JOF_DETECTING) OPDEF(JSOP_XMLNAME, 172,"xmlname", NULL, 1, 1, 1, 19, JOF_BYTE) OPDEF(JSOP_DESCENDANTS, 173,"descendants",NULL, 1, 2, 1, 18, JOF_BYTE) OPDEF(JSOP_FILTER, 174,"filter", NULL, 3, 1, 1, 0, JOF_JUMP) OPDEF(JSOP_ENDFILTER, 175,"endfilter", NULL, 1, 1, 0, 18, JOF_BYTE) OPDEF(JSOP_TOXML, 176,"toxml", NULL, 1, 1, 1, 19, JOF_BYTE) OPDEF(JSOP_TOXMLLIST, 177,"toxmllist", NULL, 1, 1, 1, 19, JOF_BYTE) OPDEF(JSOP_XMLTAGEXPR, 178,"xmltagexpr", NULL, 1, 1, 1, 0, JOF_BYTE) OPDEF(JSOP_XMLELTEXPR, 179,"xmleltexpr", NULL, 1, 1, 1, 0, JOF_BYTE) OPDEF(JSOP_XMLOBJECT, 180,"xmlobject", NULL, 3, 0, 1, 19, JOF_CONST) OPDEF(JSOP_XMLCDATA, 181,"xmlcdata", NULL, 3, 0, 1, 19, JOF_CONST) OPDEF(JSOP_XMLCOMMENT, 182,"xmlcomment", NULL, 3, 0, 1, 19, JOF_CONST) OPDEF(JSOP_XMLPI, 183,"xmlpi", NULL, 3, 1, 1, 19, JOF_CONST) OPDEF(JSOP_GETMETHOD, 184,"getmethod", NULL, 3, 1, 1, 18, JOF_CONST|JOF_PROP) OPDEF(JSOP_GETFUNNS, 185,"getfunns", NULL, 1, 0, 1, 19, JOF_BYTE) OPDEF(JSOP_FOREACH, 186,"foreach", NULL, 1, 1, 1, 0, JOF_BYTE) OPDEF(JSOP_DELDESC, 187,"deldesc", NULL, 1, 2, 1, 17, JOF_BYTE |JOF_ELEM|JOF_DEL) /* * Opcodes for extended literal addressing, using unsigned 24-bit immediate * operands to hold integer operands (JSOP_UINT24), extended atom indexes in * script->atomMap (JSOP_LITERAL, JSOP_FINDNAME), and ops prefixed by such * atom index immediates (JSOP_LITOPX). See jsemit.c, EmitAtomIndexOp. */ OPDEF(JSOP_UINT24, 188,"uint24", NULL, 4, 0, 1, 16, JOF_UINT24) OPDEF(JSOP_LITERAL, 189,"literal", NULL, 4, 0, 1, 19, JOF_UINT24) OPDEF(JSOP_FINDNAME, 190,"findname", NULL, 4, 0, 2, 0, JOF_UINT24) OPDEF(JSOP_LITOPX, 191,"litopx", NULL, 5, 0, 0, 0, JOF_LITOPX) /* * Opcodes to help the decompiler deal with XML. */ OPDEF(JSOP_STARTXML, 192,"startxml", NULL, 1, 0, 0, 0, JOF_BYTE) OPDEF(JSOP_STARTXMLEXPR, 193,"startxmlexpr",NULL, 1, 0, 0, 0, JOF_BYTE) OPDEF(JSOP_SETMETHOD, 194,"setmethod", NULL, 3, 2, 1, 3, JOF_CONST|JOF_PROP|JOF_SET|JOF_ASSIGNING|JOF_DETECTING) /* * Stop interpretation, emitted at end of script to save the threaded bytecode * interpreter an extra branch test on every DO_NEXT_OP (see jsinterp.c). */ OPDEF(JSOP_STOP, 195,"stop", NULL, 1, 0, 0, 0, JOF_BYTE) /* * Get an extant property or element value, throwing ReferenceError if the * identified property does not exist. */ OPDEF(JSOP_GETXPROP, 196,"getxprop", NULL, 3, 1, 1, 18, JOF_CONST|JOF_PROP) OPDEF(JSOP_GETXELEM, 197,"getxelem", NULL, 1, 2, 1, 18, JOF_BYTE |JOF_ELEM|JOF_LEFTASSOC) /* * Specialized JSOP_TYPEOF to avoid reporting undefined for typeof(0, undef). */ OPDEF(JSOP_TYPEOFEXPR, 198,js_typeof_str, NULL, 1, 1, 1, 15, JOF_BYTE|JOF_DETECTING) /* * Block-local scope support. */ OPDEF(JSOP_ENTERBLOCK, 199,"enterblock", NULL, 3, 0, 0, 0, JOF_CONST) OPDEF(JSOP_LEAVEBLOCK, 200,"leaveblock", NULL, 3, 0, 0, 0, JOF_UINT16) OPDEF(JSOP_GETLOCAL, 201,"getlocal", NULL, 3, 0, 1, 19, JOF_LOCAL|JOF_NAME) OPDEF(JSOP_SETLOCAL, 202,"setlocal", NULL, 3, 1, 1, 3, JOF_LOCAL|JOF_NAME|JOF_SET) OPDEF(JSOP_INCLOCAL, 203,"inclocal", NULL, 3, 0, 1, 15, JOF_LOCAL|JOF_NAME|JOF_INC) OPDEF(JSOP_DECLOCAL, 204,"declocal", NULL, 3, 0, 1, 15, JOF_LOCAL|JOF_NAME|JOF_DEC) OPDEF(JSOP_LOCALINC, 205,"localinc", NULL, 3, 0, 1, 15, JOF_LOCAL|JOF_NAME|JOF_INC|JOF_POST) OPDEF(JSOP_LOCALDEC, 206,"localdec", NULL, 3, 0, 1, 15, JOF_LOCAL|JOF_NAME|JOF_DEC|JOF_POST) OPDEF(JSOP_FORLOCAL, 207,"forlocal", NULL, 3, 0, 1, 19, JOF_LOCAL|JOF_NAME|JOF_FOR) /* * Iterator, generator, and array comprehension support. */ OPDEF(JSOP_STARTITER, 208,"startiter", NULL, 1, 0, 0, 0, JOF_BYTE) OPDEF(JSOP_ENDITER, 209,"enditer", NULL, 1, 1, 0, 0, JOF_BYTE) OPDEF(JSOP_GENERATOR, 210,"generator", NULL, 1, 0, 0, 0, JOF_BYTE) OPDEF(JSOP_YIELD, 211,"yield", NULL, 1, 1, 1, 1, JOF_BYTE) OPDEF(JSOP_ARRAYPUSH, 212,"arraypush", NULL, 3, 1, 0, 3, JOF_LOCAL) OPDEF(JSOP_FOREACHKEYVAL, 213,"foreachkeyval",NULL, 1, 1, 1, 0, JOF_BYTE) /* * Variant of JSOP_ENUMELEM for destructuring const (const [a, b] = ...). */ OPDEF(JSOP_ENUMCONSTELEM, 214,"enumconstelem",NULL, 1, 3, 0, 3, JOF_BYTE|JOF_SET|JOF_ASSIGNING) /* * Variant of JSOP_LEAVEBLOCK has a result on the stack above the locals, * which must be moved down when the block pops. */ OPDEF(JSOP_LEAVEBLOCKEXPR,215,"leaveblockexpr",NULL, 3, 0, 0, 1, JOF_UINT16) pacparser-1.4.5/src/spidermonkey/js/src/jsosdep.h000066400000000000000000000057211464010763600220400ustar00rootroot00000000000000/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- * * ***** BEGIN LICENSE BLOCK ***** * Version: MPL 1.1/GPL 2.0/LGPL 2.1 * * The contents of this file are subject to the Mozilla Public License Version * 1.1 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * http://www.mozilla.org/MPL/ * * Software distributed under the License is distributed on an "AS IS" basis, * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License * for the specific language governing rights and limitations under the * License. * * The Original Code is Mozilla Communicator client code, released * March 31, 1998. * * The Initial Developer of the Original Code is * Netscape Communications Corporation. * Portions created by the Initial Developer are Copyright (C) 1998 * the Initial Developer. All Rights Reserved. * * Contributor(s): * * Alternatively, the contents of this file may be used under the terms of * either of the GNU General Public License Version 2 or later (the "GPL"), * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), * in which case the provisions of the GPL or the LGPL are applicable instead * of those above. If you wish to allow use of your version of this file only * under the terms of either the GPL or the LGPL, and not to allow others to * use your version of this file under the terms of the MPL, indicate your * decision by deleting the provisions above and replace them with the notice * and other provisions required by the GPL or the LGPL. If you do not delete * the provisions above, a recipient may use your version of this file under * the terms of any one of the MPL, the GPL or the LGPL. * * ***** END LICENSE BLOCK ***** */ #ifndef jsosdep_h___ #define jsosdep_h___ /* * OS (and machine, and compiler XXX) dependent information. */ #if defined(XP_WIN) || defined(XP_OS2) #if defined(_WIN32) || defined (XP_OS2) #define JS_HAVE_LONG_LONG #else #undef JS_HAVE_LONG_LONG #endif #endif /* XP_WIN || XP_OS2 */ #ifdef XP_BEOS #define JS_HAVE_LONG_LONG #endif #ifdef XP_UNIX /* * Get OS specific header information. */ #if defined(XP_MACOSX) || defined(DARWIN) #define JS_HAVE_LONG_LONG #elif defined(AIXV3) || defined(AIX) #define JS_HAVE_LONG_LONG #elif defined(BSDI) #define JS_HAVE_LONG_LONG #elif defined(HPUX) #define JS_HAVE_LONG_LONG #elif defined(IRIX) #define JS_HAVE_LONG_LONG #elif defined(linux) #define JS_HAVE_LONG_LONG #elif defined(OSF1) #define JS_HAVE_LONG_LONG #elif defined(_SCO_DS) #undef JS_HAVE_LONG_LONG #elif defined(SOLARIS) #define JS_HAVE_LONG_LONG #elif defined(FREEBSD) #define JS_HAVE_LONG_LONG #elif defined(SUNOS4) #undef JS_HAVE_LONG_LONG /* ** Missing function prototypes */ extern void *sbrk(int); #elif defined(UNIXWARE) #undef JS_HAVE_LONG_LONG #elif defined(VMS) && defined(__ALPHA) #define JS_HAVE_LONG_LONG #endif #endif /* XP_UNIX */ #endif /* jsosdep_h___ */ pacparser-1.4.5/src/spidermonkey/js/src/jsotypes.h000066400000000000000000000157661464010763600222630ustar00rootroot00000000000000/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* ***** BEGIN LICENSE BLOCK ***** * Version: MPL 1.1/GPL 2.0/LGPL 2.1 * * The contents of this file are subject to the Mozilla Public License Version * 1.1 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * http://www.mozilla.org/MPL/ * * Software distributed under the License is distributed on an "AS IS" basis, * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License * for the specific language governing rights and limitations under the * License. * * The Original Code is Mozilla Communicator client code, released * March 31, 1998. * * The Initial Developer of the Original Code is * Netscape Communications Corporation. * Portions created by the Initial Developer are Copyright (C) 1998 * the Initial Developer. All Rights Reserved. * * Contributor(s): * * Alternatively, the contents of this file may be used under the terms of * either of the GNU General Public License Version 2 or later (the "GPL"), * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), * in which case the provisions of the GPL or the LGPL are applicable instead * of those above. If you wish to allow use of your version of this file only * under the terms of either the GPL or the LGPL, and not to allow others to * use your version of this file under the terms of the MPL, indicate your * decision by deleting the provisions above and replace them with the notice * and other provisions required by the GPL or the LGPL. If you do not delete * the provisions above, a recipient may use your version of this file under * the terms of any one of the MPL, the GPL or the LGPL. * * ***** END LICENSE BLOCK ***** */ /* * This section typedefs the old 'native' types to the new PRs. * These definitions are scheduled to be eliminated at the earliest * possible time. The NSPR API is implemented and documented using * the new definitions. */ /* * Note that we test for PROTYPES_H, not JSOTYPES_H. This is to avoid * double-definitions of scalar types such as uint32, if NSPR's * protypes.h is also included. */ #ifndef PROTYPES_H #define PROTYPES_H #ifdef XP_BEOS /* BeOS defines most int types in SupportDefs.h (int8, uint8, int16, * uint16, int32, uint32, int64, uint64), so in the interest of * not conflicting with other definitions elsewhere we have to skip the * #ifdef jungle below, duplicate some definitions, and do our stuff. */ #include typedef JSUintn uintn; #ifndef _XP_Core_ typedef JSIntn intn; #endif #else /* SVR4 typedef of uint is commonly found on UNIX machines. */ #if defined(XP_UNIX) && !defined(__QNXNTO__) #include #else typedef JSUintn uint; #endif typedef JSUintn uintn; typedef JSUint64 uint64; #if !defined(_WIN32) && !defined(XP_OS2) typedef JSUint32 uint32; #else typedef unsigned long uint32; #endif typedef JSUint16 uint16; typedef JSUint8 uint8; #ifndef _XP_Core_ typedef JSIntn intn; #endif /* * On AIX 4.3, sys/inttypes.h (which is included by sys/types.h, a very * common header file) defines the types int8, int16, int32, and int64. * So we don't define these four types here to avoid conflicts in case * the code also includes sys/types.h. */ #if defined(AIX) && defined(HAVE_SYS_INTTYPES_H) #include #else typedef JSInt64 int64; /* /usr/include/model.h on HP-UX defines int8, int16, and int32 */ #ifdef HPUX #include #else #if !defined(_WIN32) && !defined(XP_OS2) typedef JSInt32 int32; #else typedef long int32; #endif typedef JSInt16 int16; typedef JSInt8 int8; #endif /* HPUX */ #endif /* AIX && HAVE_SYS_INTTYPES_H */ #endif /* XP_BEOS */ typedef JSFloat64 float64; /* Re: jsbit.h */ #define TEST_BIT JS_TEST_BIT #define SET_BIT JS_SET_BIT #define CLEAR_BIT JS_CLEAR_BIT /* Re: prarena.h->plarena.h */ #define PRArena PLArena #define PRArenaPool PLArenaPool #define PRArenaStats PLArenaStats #define PR_ARENA_ALIGN PL_ARENA_ALIGN #define PR_INIT_ARENA_POOL PL_INIT_ARENA_POOL #define PR_ARENA_ALLOCATE PL_ARENA_ALLOCATE #define PR_ARENA_GROW PL_ARENA_GROW #define PR_ARENA_MARK PL_ARENA_MARK #define PR_CLEAR_UNUSED PL_CLEAR_UNUSED #define PR_CLEAR_ARENA PL_CLEAR_ARENA #define PR_ARENA_RELEASE PL_ARENA_RELEASE #define PR_COUNT_ARENA PL_COUNT_ARENA #define PR_ARENA_DESTROY PL_ARENA_DESTROY #define PR_InitArenaPool PL_InitArenaPool #define PR_FreeArenaPool PL_FreeArenaPool #define PR_FinishArenaPool PL_FinishArenaPool #define PR_CompactArenaPool PL_CompactArenaPool #define PR_ArenaFinish PL_ArenaFinish #define PR_ArenaAllocate PL_ArenaAllocate #define PR_ArenaGrow PL_ArenaGrow #define PR_ArenaRelease PL_ArenaRelease #define PR_ArenaCountAllocation PL_ArenaCountAllocation #define PR_ArenaCountInplaceGrowth PL_ArenaCountInplaceGrowth #define PR_ArenaCountGrowth PL_ArenaCountGrowth #define PR_ArenaCountRelease PL_ArenaCountRelease #define PR_ArenaCountRetract PL_ArenaCountRetract /* Re: prevent.h->plevent.h */ #define PREvent PLEvent #define PREventQueue PLEventQueue #define PR_CreateEventQueue PL_CreateEventQueue #define PR_DestroyEventQueue PL_DestroyEventQueue #define PR_GetEventQueueMonitor PL_GetEventQueueMonitor #define PR_ENTER_EVENT_QUEUE_MONITOR PL_ENTER_EVENT_QUEUE_MONITOR #define PR_EXIT_EVENT_QUEUE_MONITOR PL_EXIT_EVENT_QUEUE_MONITOR #define PR_PostEvent PL_PostEvent #define PR_PostSynchronousEvent PL_PostSynchronousEvent #define PR_GetEvent PL_GetEvent #define PR_EventAvailable PL_EventAvailable #define PREventFunProc PLEventFunProc #define PR_MapEvents PL_MapEvents #define PR_RevokeEvents PL_RevokeEvents #define PR_ProcessPendingEvents PL_ProcessPendingEvents #define PR_WaitForEvent PL_WaitForEvent #define PR_EventLoop PL_EventLoop #define PR_GetEventQueueSelectFD PL_GetEventQueueSelectFD #define PRHandleEventProc PLHandleEventProc #define PRDestroyEventProc PLDestroyEventProc #define PR_InitEvent PL_InitEvent #define PR_GetEventOwner PL_GetEventOwner #define PR_HandleEvent PL_HandleEvent #define PR_DestroyEvent PL_DestroyEvent #define PR_DequeueEvent PL_DequeueEvent #define PR_GetMainEventQueue PL_GetMainEventQueue /* Re: prhash.h->plhash.h */ #define PRHashEntry PLHashEntry #define PRHashTable PLHashTable #define PRHashNumber PLHashNumber #define PRHashFunction PLHashFunction #define PRHashComparator PLHashComparator #define PRHashEnumerator PLHashEnumerator #define PRHashAllocOps PLHashAllocOps #define PR_NewHashTable PL_NewHashTable #define PR_HashTableDestroy PL_HashTableDestroy #define PR_HashTableRawLookup PL_HashTableRawLookup #define PR_HashTableRawAdd PL_HashTableRawAdd #define PR_HashTableRawRemove PL_HashTableRawRemove #define PR_HashTableAdd PL_HashTableAdd #define PR_HashTableRemove PL_HashTableRemove #define PR_HashTableEnumerateEntries PL_HashTableEnumerateEntries #define PR_HashTableLookup PL_HashTableLookup #define PR_HashTableDump PL_HashTableDump #define PR_HashString PL_HashString #define PR_CompareStrings PL_CompareStrings #define PR_CompareValues PL_CompareValues #endif /* !defined(PROTYPES_H) */ pacparser-1.4.5/src/spidermonkey/js/src/jsparse.c000066400000000000000000006427371464010763600220510ustar00rootroot00000000000000/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- * vim: set ts=8 sw=4 et tw=78: * * ***** BEGIN LICENSE BLOCK ***** * Version: MPL 1.1/GPL 2.0/LGPL 2.1 * * The contents of this file are subject to the Mozilla Public License Version * 1.1 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * http://www.mozilla.org/MPL/ * * Software distributed under the License is distributed on an "AS IS" basis, * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License * for the specific language governing rights and limitations under the * License. * * The Original Code is Mozilla Communicator client code, released * March 31, 1998. * * The Initial Developer of the Original Code is * Netscape Communications Corporation. * Portions created by the Initial Developer are Copyright (C) 1998 * the Initial Developer. All Rights Reserved. * * Contributor(s): * * Alternatively, the contents of this file may be used under the terms of * either of the GNU General Public License Version 2 or later (the "GPL"), * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), * in which case the provisions of the GPL or the LGPL are applicable instead * of those above. If you wish to allow use of your version of this file only * under the terms of either the GPL or the LGPL, and not to allow others to * use your version of this file under the terms of the MPL, indicate your * decision by deleting the provisions above and replace them with the notice * and other provisions required by the GPL or the LGPL. If you do not delete * the provisions above, a recipient may use your version of this file under * the terms of any one of the MPL, the GPL or the LGPL. * * ***** END LICENSE BLOCK ***** */ /* * JS parser. * * This is a recursive-descent parser for the JavaScript language specified by * "The JavaScript 1.5 Language Specification". It uses lexical and semantic * feedback to disambiguate non-LL(1) structures. It generates trees of nodes * induced by the recursive parsing (not precise syntax trees, see jsparse.h). * After tree construction, it rewrites trees to fold constants and evaluate * compile-time expressions. Finally, it calls js_EmitTree (see jsemit.h) to * generate bytecode. * * This parser attempts no error recovery. */ #include "jsstddef.h" #include #include #include #include "jstypes.h" #include "jsarena.h" /* Added by JSIFY */ #include "jsutil.h" /* Added by JSIFY */ #include "jsapi.h" #include "jsarray.h" #include "jsatom.h" #include "jscntxt.h" #include "jsconfig.h" #include "jsemit.h" #include "jsfun.h" #include "jsinterp.h" #include "jslock.h" #include "jsnum.h" #include "jsobj.h" #include "jsopcode.h" #include "jsparse.h" #include "jsscan.h" #include "jsscope.h" #include "jsscript.h" #include "jsstr.h" #if JS_HAS_XML_SUPPORT #include "jsxml.h" #endif #if JS_HAS_DESTRUCTURING #include "jsdhash.h" #endif /* * JS parsers, from lowest to highest precedence. * * Each parser takes a context, a token stream, and a tree context struct. * Each returns a parse node tree or null on error. */ typedef JSParseNode * JSParser(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc); typedef JSParseNode * JSMemberParser(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc, JSBool allowCallSyntax); typedef JSParseNode * JSPrimaryParser(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc, JSTokenType tt, JSBool afterDot); static JSParser FunctionStmt; static JSParser FunctionExpr; static JSParser Statements; static JSParser Statement; static JSParser Variables; static JSParser Expr; static JSParser AssignExpr; static JSParser CondExpr; static JSParser OrExpr; static JSParser AndExpr; static JSParser BitOrExpr; static JSParser BitXorExpr; static JSParser BitAndExpr; static JSParser EqExpr; static JSParser RelExpr; static JSParser ShiftExpr; static JSParser AddExpr; static JSParser MulExpr; static JSParser UnaryExpr; static JSMemberParser MemberExpr; static JSPrimaryParser PrimaryExpr; /* * Insist that the next token be of type tt, or report errno and return null. * NB: this macro uses cx and ts from its lexical environment. */ #define MUST_MATCH_TOKEN(tt, errno) \ JS_BEGIN_MACRO \ if (js_GetToken(cx, ts) != tt) { \ js_ReportCompileErrorNumber(cx, ts, JSREPORT_TS | JSREPORT_ERROR, \ errno); \ return NULL; \ } \ JS_END_MACRO #define CHECK_RECURSION() \ JS_BEGIN_MACRO \ int stackDummy; \ if (!JS_CHECK_STACK_SIZE(cx, stackDummy)) { \ js_ReportCompileErrorNumber(cx, ts, JSREPORT_TS | JSREPORT_ERROR, \ JSMSG_OVER_RECURSED); \ return NULL; \ } \ JS_END_MACRO #ifdef METER_PARSENODES static uint32 parsenodes = 0; static uint32 maxparsenodes = 0; static uint32 recyclednodes = 0; #endif static JSParseNode * RecycleTree(JSParseNode *pn, JSTreeContext *tc) { JSParseNode *next; if (!pn) return NULL; JS_ASSERT(pn != tc->nodeList); /* catch back-to-back dup recycles */ next = pn->pn_next; pn->pn_next = tc->nodeList; tc->nodeList = pn; #ifdef METER_PARSENODES recyclednodes++; #endif return next; } static JSParseNode * NewOrRecycledNode(JSContext *cx, JSTreeContext *tc) { JSParseNode *pn; pn = tc->nodeList; if (!pn) { JS_ARENA_ALLOCATE_TYPE(pn, JSParseNode, &cx->tempPool); if (!pn) JS_ReportOutOfMemory(cx); } else { tc->nodeList = pn->pn_next; /* Recycle immediate descendents only, to save work and working set. */ switch (pn->pn_arity) { case PN_FUNC: RecycleTree(pn->pn_body, tc); break; case PN_LIST: if (pn->pn_head) { /* XXX check for dup recycles in the list */ *pn->pn_tail = tc->nodeList; tc->nodeList = pn->pn_head; #ifdef METER_PARSENODES recyclednodes += pn->pn_count; #endif } break; case PN_TERNARY: RecycleTree(pn->pn_kid1, tc); RecycleTree(pn->pn_kid2, tc); RecycleTree(pn->pn_kid3, tc); break; case PN_BINARY: RecycleTree(pn->pn_left, tc); RecycleTree(pn->pn_right, tc); break; case PN_UNARY: RecycleTree(pn->pn_kid, tc); break; case PN_NAME: RecycleTree(pn->pn_expr, tc); break; case PN_NULLARY: break; } } #ifdef METER_PARSENODES if (pn) { parsenodes++; if (parsenodes - recyclednodes > maxparsenodes) maxparsenodes = parsenodes - recyclednodes; } #endif return pn; } /* * Allocate a JSParseNode from cx's temporary arena. */ static JSParseNode * NewParseNode(JSContext *cx, JSTokenStream *ts, JSParseNodeArity arity, JSTreeContext *tc) { JSParseNode *pn; JSToken *tp; pn = NewOrRecycledNode(cx, tc); if (!pn) return NULL; tp = &CURRENT_TOKEN(ts); pn->pn_type = tp->type; pn->pn_pos = tp->pos; pn->pn_op = JSOP_NOP; pn->pn_arity = arity; pn->pn_next = NULL; pn->pn_ts = ts; pn->pn_source = NULL; return pn; } static JSParseNode * NewBinary(JSContext *cx, JSTokenType tt, JSOp op, JSParseNode *left, JSParseNode *right, JSTreeContext *tc) { JSParseNode *pn, *pn1, *pn2; if (!left || !right) return NULL; /* * Flatten a left-associative (left-heavy) tree of a given operator into * a list, to reduce js_FoldConstants and js_EmitTree recursion. */ if (left->pn_type == tt && left->pn_op == op && (js_CodeSpec[op].format & JOF_LEFTASSOC)) { if (left->pn_arity != PN_LIST) { pn1 = left->pn_left, pn2 = left->pn_right; left->pn_arity = PN_LIST; PN_INIT_LIST_1(left, pn1); PN_APPEND(left, pn2); if (tt == TOK_PLUS) { if (pn1->pn_type == TOK_STRING) left->pn_extra |= PNX_STRCAT; else if (pn1->pn_type != TOK_NUMBER) left->pn_extra |= PNX_CANTFOLD; if (pn2->pn_type == TOK_STRING) left->pn_extra |= PNX_STRCAT; else if (pn2->pn_type != TOK_NUMBER) left->pn_extra |= PNX_CANTFOLD; } } PN_APPEND(left, right); left->pn_pos.end = right->pn_pos.end; if (tt == TOK_PLUS) { if (right->pn_type == TOK_STRING) left->pn_extra |= PNX_STRCAT; else if (right->pn_type != TOK_NUMBER) left->pn_extra |= PNX_CANTFOLD; } return left; } /* * Fold constant addition immediately, to conserve node space and, what's * more, so js_FoldConstants never sees mixed addition and concatenation * operations with more than one leading non-string operand in a PN_LIST * generated for expressions such as 1 + 2 + "pt" (which should evaluate * to "3pt", not "12pt"). */ if (tt == TOK_PLUS && left->pn_type == TOK_NUMBER && right->pn_type == TOK_NUMBER) { left->pn_dval += right->pn_dval; left->pn_pos.end = right->pn_pos.end; RecycleTree(right, tc); return left; } pn = NewOrRecycledNode(cx, tc); if (!pn) return NULL; pn->pn_type = tt; pn->pn_pos.begin = left->pn_pos.begin; pn->pn_pos.end = right->pn_pos.end; pn->pn_op = op; pn->pn_arity = PN_BINARY; pn->pn_left = left; pn->pn_right = right; pn->pn_next = NULL; pn->pn_ts = NULL; pn->pn_source = NULL; return pn; } #if JS_HAS_GETTER_SETTER static JSTokenType CheckGetterOrSetter(JSContext *cx, JSTokenStream *ts, JSTokenType tt) { JSAtom *atom; JSRuntime *rt; JSOp op; const char *name; JS_ASSERT(CURRENT_TOKEN(ts).type == TOK_NAME); atom = CURRENT_TOKEN(ts).t_atom; rt = cx->runtime; if (atom == rt->atomState.getterAtom) op = JSOP_GETTER; else if (atom == rt->atomState.setterAtom) op = JSOP_SETTER; else return TOK_NAME; if (js_PeekTokenSameLine(cx, ts) != tt) return TOK_NAME; (void) js_GetToken(cx, ts); if (CURRENT_TOKEN(ts).t_op != JSOP_NOP) { js_ReportCompileErrorNumber(cx, ts, JSREPORT_TS | JSREPORT_ERROR, JSMSG_BAD_GETTER_OR_SETTER, (op == JSOP_GETTER) ? js_getter_str : js_setter_str); return TOK_ERROR; } CURRENT_TOKEN(ts).t_op = op; if (JS_HAS_STRICT_OPTION(cx)) { name = js_AtomToPrintableString(cx, atom); if (!name || !js_ReportCompileErrorNumber(cx, ts, JSREPORT_TS | JSREPORT_WARNING | JSREPORT_STRICT, JSMSG_DEPRECATED_USAGE, name)) { return TOK_ERROR; } } return tt; } #endif static void MaybeSetupFrame(JSContext *cx, JSObject *chain, JSStackFrame *oldfp, JSStackFrame *newfp) { /* * Always push a new frame if the current frame is special, so that * Variables gets the correct variables object: the one from the special * frame's caller. */ if (oldfp && oldfp->varobj && oldfp->scopeChain == chain && !(oldfp->flags & JSFRAME_SPECIAL)) { return; } memset(newfp, 0, sizeof *newfp); /* Default to sharing the same variables object and scope chain. */ newfp->varobj = newfp->scopeChain = chain; if (cx->options & JSOPTION_VAROBJFIX) { while ((chain = JS_GetParent(cx, chain)) != NULL) newfp->varobj = chain; } newfp->down = oldfp; if (oldfp) { /* * In the case of eval and debugger frames, we need to dig down and find * the real variables objects and function that our new stack frame is * going to use. */ newfp->flags = oldfp->flags & (JSFRAME_SPECIAL | JSFRAME_COMPILE_N_GO | JSFRAME_SCRIPT_OBJECT); while (oldfp->flags & JSFRAME_SPECIAL) { oldfp = oldfp->down; if (!oldfp) break; } if (oldfp && (newfp->flags & JSFRAME_SPECIAL)) { newfp->varobj = oldfp->varobj; newfp->vars = oldfp->vars; newfp->fun = oldfp->fun; } } cx->fp = newfp; } /* * Parse a top-level JS script. */ JS_FRIEND_API(JSParseNode *) js_ParseTokenStream(JSContext *cx, JSObject *chain, JSTokenStream *ts) { JSStackFrame *fp, frame; JSTreeContext tc; JSParseNode *pn; /* * Push a compiler frame if we have no frames, or if the top frame is a * lightweight function activation, or if its scope chain doesn't match * the one passed to us. */ fp = cx->fp; MaybeSetupFrame(cx, chain, fp, &frame); /* * Protect atoms from being collected by a GC activation, which might * - nest on this thread due to out of memory (the so-called "last ditch" * GC attempted within js_NewGCThing), or * - run for any reason on another thread if this thread is suspended on * an object lock before it finishes generating bytecode into a script * protected from the GC by a root or a stack frame reference. */ JS_KEEP_ATOMS(cx->runtime); TREE_CONTEXT_INIT(&tc); pn = Statements(cx, ts, &tc); if (pn) { if (!js_MatchToken(cx, ts, TOK_EOF)) { js_ReportCompileErrorNumber(cx, ts, JSREPORT_TS | JSREPORT_ERROR, JSMSG_SYNTAX_ERROR); pn = NULL; } else { pn->pn_type = TOK_LC; if (!js_FoldConstants(cx, pn, &tc)) pn = NULL; } } TREE_CONTEXT_FINISH(&tc); JS_UNKEEP_ATOMS(cx->runtime); cx->fp = fp; return pn; } /* * Compile a top-level script. */ JS_FRIEND_API(JSBool) js_CompileTokenStream(JSContext *cx, JSObject *chain, JSTokenStream *ts, JSCodeGenerator *cg) { JSStackFrame *fp, frame; uint32 flags; JSParseNode *pn; JSBool ok; #ifdef METER_PARSENODES void *sbrk(ptrdiff_t), *before = sbrk(0); #endif /* * Push a compiler frame if we have no frames, or if the top frame is a * lightweight function activation, or if its scope chain doesn't match * the one passed to us. */ fp = cx->fp; MaybeSetupFrame(cx, chain, fp, &frame); flags = cx->fp->flags; cx->fp->flags = flags | (JS_HAS_COMPILE_N_GO_OPTION(cx) ? JSFRAME_COMPILING | JSFRAME_COMPILE_N_GO : JSFRAME_COMPILING); /* Prevent GC activation while compiling. */ JS_KEEP_ATOMS(cx->runtime); pn = Statements(cx, ts, &cg->treeContext); if (!pn) { ok = JS_FALSE; } else if (!js_MatchToken(cx, ts, TOK_EOF)) { js_ReportCompileErrorNumber(cx, ts, JSREPORT_TS | JSREPORT_ERROR, JSMSG_SYNTAX_ERROR); ok = JS_FALSE; } else { #ifdef METER_PARSENODES printf("Parser growth: %d (%u nodes, %u max, %u unrecycled)\n", (char *)sbrk(0) - (char *)before, parsenodes, maxparsenodes, parsenodes - recyclednodes); before = sbrk(0); #endif /* * No need to emit bytecode here -- Statements already has, for each * statement in turn. Search for TCF_COMPILING in Statements, below. * That flag is set for every tc == &cg->treeContext, and it implies * that the tc can be downcast to a cg and used to emit code during * parsing, rather than at the end of the parse phase. * * Nowadays the threaded interpreter needs a stop instruction, so we * do have to emit that here. */ JS_ASSERT(cg->treeContext.flags & TCF_COMPILING); ok = js_Emit1(cx, cg, JSOP_STOP) >= 0; } #ifdef METER_PARSENODES printf("Code-gen growth: %d (%u bytecodes, %u srcnotes)\n", (char *)sbrk(0) - (char *)before, CG_OFFSET(cg), cg->noteCount); #endif #ifdef JS_ARENAMETER JS_DumpArenaStats(stdout); #endif JS_UNKEEP_ATOMS(cx->runtime); cx->fp->flags = flags; cx->fp = fp; return ok; } /* * Insist on a final return before control flows out of pn. Try to be a bit * smart about loops: do {...; return e2;} while(0) at the end of a function * that contains an early return e1 will get a strict warning. Similarly for * iloops: while (true){...} is treated as though ... returns. */ #define ENDS_IN_OTHER 0 #define ENDS_IN_RETURN 1 #define ENDS_IN_BREAK 2 static int HasFinalReturn(JSParseNode *pn) { JSParseNode *pn2, *pn3; uintN rv, rv2, hasDefault; switch (pn->pn_type) { case TOK_LC: if (!pn->pn_head) return ENDS_IN_OTHER; return HasFinalReturn(PN_LAST(pn)); case TOK_IF: if (!pn->pn_kid3) return ENDS_IN_OTHER; return HasFinalReturn(pn->pn_kid2) & HasFinalReturn(pn->pn_kid3); case TOK_WHILE: pn2 = pn->pn_left; if (pn2->pn_type == TOK_PRIMARY && pn2->pn_op == JSOP_TRUE) return ENDS_IN_RETURN; if (pn2->pn_type == TOK_NUMBER && pn2->pn_dval) return ENDS_IN_RETURN; return ENDS_IN_OTHER; case TOK_DO: pn2 = pn->pn_right; if (pn2->pn_type == TOK_PRIMARY) { if (pn2->pn_op == JSOP_FALSE) return HasFinalReturn(pn->pn_left); if (pn2->pn_op == JSOP_TRUE) return ENDS_IN_RETURN; } if (pn2->pn_type == TOK_NUMBER) { if (pn2->pn_dval == 0) return HasFinalReturn(pn->pn_left); return ENDS_IN_RETURN; } return ENDS_IN_OTHER; case TOK_FOR: pn2 = pn->pn_left; if (pn2->pn_arity == PN_TERNARY && !pn2->pn_kid2) return ENDS_IN_RETURN; return ENDS_IN_OTHER; case TOK_SWITCH: rv = ENDS_IN_RETURN; hasDefault = ENDS_IN_OTHER; pn2 = pn->pn_right; if (pn2->pn_type == TOK_LEXICALSCOPE) pn2 = pn2->pn_expr; for (pn2 = pn2->pn_head; rv && pn2; pn2 = pn2->pn_next) { if (pn2->pn_type == TOK_DEFAULT) hasDefault = ENDS_IN_RETURN; pn3 = pn2->pn_right; JS_ASSERT(pn3->pn_type == TOK_LC); if (pn3->pn_head) { rv2 = HasFinalReturn(PN_LAST(pn3)); if (rv2 == ENDS_IN_OTHER && pn2->pn_next) /* Falling through to next case or default. */; else rv &= rv2; } } /* If a final switch has no default case, we judge it harshly. */ rv &= hasDefault; return rv; case TOK_BREAK: return ENDS_IN_BREAK; case TOK_WITH: return HasFinalReturn(pn->pn_right); case TOK_RETURN: return ENDS_IN_RETURN; case TOK_COLON: case TOK_LEXICALSCOPE: return HasFinalReturn(pn->pn_expr); case TOK_THROW: return ENDS_IN_RETURN; case TOK_TRY: /* If we have a finally block that returns, we are done. */ if (pn->pn_kid3) { rv = HasFinalReturn(pn->pn_kid3); if (rv == ENDS_IN_RETURN) return rv; } /* Else check the try block and any and all catch statements. */ rv = HasFinalReturn(pn->pn_kid1); if (pn->pn_kid2) { JS_ASSERT(pn->pn_kid2->pn_arity == PN_LIST); for (pn2 = pn->pn_kid2->pn_head; pn2; pn2 = pn2->pn_next) rv &= HasFinalReturn(pn2); } return rv; case TOK_CATCH: /* Check this catch block's body. */ return HasFinalReturn(pn->pn_kid3); case TOK_LET: /* Non-binary let statements are let declarations. */ if (pn->pn_arity != PN_BINARY) return ENDS_IN_OTHER; return HasFinalReturn(pn->pn_right); default: return ENDS_IN_OTHER; } } static JSBool ReportBadReturn(JSContext *cx, JSTokenStream *ts, uintN flags, uintN errnum, uintN anonerrnum) { JSFunction *fun; const char *name; fun = cx->fp->fun; if (fun->atom) { name = js_AtomToPrintableString(cx, fun->atom); } else { errnum = anonerrnum; name = NULL; } return js_ReportCompileErrorNumber(cx, ts, JSREPORT_TS | flags, errnum, name); } static JSBool CheckFinalReturn(JSContext *cx, JSTokenStream *ts, JSParseNode *pn) { return HasFinalReturn(pn) == ENDS_IN_RETURN || ReportBadReturn(cx, ts, JSREPORT_WARNING | JSREPORT_STRICT, JSMSG_NO_RETURN_VALUE, JSMSG_ANON_NO_RETURN_VALUE); } static JSParseNode * FunctionBody(JSContext *cx, JSTokenStream *ts, JSFunction *fun, JSTreeContext *tc) { JSStackFrame *fp, frame; JSObject *funobj; JSStmtInfo stmtInfo; uintN oldflags, firstLine; JSParseNode *pn; fp = cx->fp; funobj = fun->object; if (!fp || fp->fun != fun || fp->varobj != funobj || fp->scopeChain != funobj) { memset(&frame, 0, sizeof frame); frame.fun = fun; frame.varobj = frame.scopeChain = funobj; frame.down = fp; if (fp) frame.flags = fp->flags & JSFRAME_COMPILE_N_GO; cx->fp = &frame; } /* * Set interpreted early so js_EmitTree can test it to decide whether to * eliminate useless expressions. */ fun->flags |= JSFUN_INTERPRETED; js_PushStatement(tc, &stmtInfo, STMT_BLOCK, -1); stmtInfo.flags = SIF_BODY_BLOCK; oldflags = tc->flags; tc->flags &= ~(TCF_RETURN_EXPR | TCF_RETURN_VOID); tc->flags |= TCF_IN_FUNCTION; /* * Save the body's first line, and store it in pn->pn_pos.begin.lineno * later, because we may have not peeked in ts yet, so Statements won't * acquire a valid pn->pn_pos.begin from the current token. */ firstLine = ts->lineno; pn = Statements(cx, ts, tc); js_PopStatement(tc); /* Check for falling off the end of a function that returns a value. */ if (pn && JS_HAS_STRICT_OPTION(cx) && (tc->flags & TCF_RETURN_EXPR)) { if (!CheckFinalReturn(cx, ts, pn)) pn = NULL; } /* * If we have a parse tree in pn and a code generator in tc, emit this * function's code. We must do this here, not in js_CompileFunctionBody, * in order to detect TCF_IN_FUNCTION among tc->flags. */ if (pn) { pn->pn_pos.begin.lineno = firstLine; if ((tc->flags & TCF_COMPILING)) { JSCodeGenerator *cg = (JSCodeGenerator *) tc; if (!js_FoldConstants(cx, pn, tc) || !js_EmitFunctionBytecode(cx, cg, pn)) { pn = NULL; } } } cx->fp = fp; tc->flags = oldflags | (tc->flags & (TCF_FUN_FLAGS | TCF_HAS_DEFXMLNS)); return pn; } /* * Compile a JS function body, which might appear as the value of an event * handler attribute in an HTML tag. */ JSBool js_CompileFunctionBody(JSContext *cx, JSTokenStream *ts, JSFunction *fun) { JSArenaPool codePool, notePool; JSCodeGenerator funcg; JSStackFrame *fp, frame; JSObject *funobj; JSParseNode *pn; JS_InitArenaPool(&codePool, "code", 1024, sizeof(jsbytecode)); JS_InitArenaPool(¬ePool, "note", 1024, sizeof(jssrcnote)); if (!js_InitCodeGenerator(cx, &funcg, &codePool, ¬ePool, ts->filename, ts->lineno, ts->principals)) { return JS_FALSE; } /* Prevent GC activation while compiling. */ JS_KEEP_ATOMS(cx->runtime); /* Push a JSStackFrame for use by FunctionBody. */ fp = cx->fp; funobj = fun->object; JS_ASSERT(!fp || (fp->fun != fun && fp->varobj != funobj && fp->scopeChain != funobj)); memset(&frame, 0, sizeof frame); frame.fun = fun; frame.varobj = frame.scopeChain = funobj; frame.down = fp; frame.flags = JS_HAS_COMPILE_N_GO_OPTION(cx) ? JSFRAME_COMPILING | JSFRAME_COMPILE_N_GO : JSFRAME_COMPILING; cx->fp = &frame; /* * Farble the body so that it looks like a block statement to js_EmitTree, * which is called beneath FunctionBody; see Statements, further below in * this file. FunctionBody pushes a STMT_BLOCK record around its call to * Statements, so Statements will not compile each statement as it loops * to save JSParseNode space -- it will not compile at all, only build a * JSParseNode tree. * * Therefore we must fold constants, allocate try notes, and generate code * for this function, including a stop opcode at the end. */ CURRENT_TOKEN(ts).type = TOK_LC; pn = FunctionBody(cx, ts, fun, &funcg.treeContext); if (pn && !js_NewScriptFromCG(cx, &funcg, fun)) pn = NULL; /* Restore saved state and release code generation arenas. */ cx->fp = fp; JS_UNKEEP_ATOMS(cx->runtime); js_FinishCodeGenerator(cx, &funcg); JS_FinishArenaPool(&codePool); JS_FinishArenaPool(¬ePool); return pn != NULL; } /* * Parameter block types for the several Binder functions. We use a common * helper function signature in order to share code among destructuring and * simple variable declaration parsers. In the destructuring case, the binder * function is called indirectly from the variable declaration parser by way * of CheckDestructuring and its friends. */ typedef struct BindData BindData; typedef JSBool (*Binder)(JSContext *cx, BindData *data, JSAtom *atom, JSTreeContext *tc); struct BindData { JSParseNode *pn; /* error source coordinate */ JSTokenStream *ts; /* fallback if pn is null */ JSObject *obj; /* the variable object */ JSOp op; /* prolog bytecode or nop */ Binder binder; /* binder, discriminates u */ union { struct { JSFunction *fun; /* must come first! see next */ } arg; struct { JSFunction *fun; /* this overlays u.arg.fun */ JSClass *clasp; JSPropertyOp getter; JSPropertyOp setter; uintN attrs; } var; struct { jsuint index; uintN overflow; } let; } u; }; /* * Given BindData *data and JSREPORT_* flags, expand to the second and third * actual parameters to js_ReportCompileErrorNumber. Prefer reporting via pn * to reporting via ts, for better destructuring error pointers. */ #define BIND_DATA_REPORT_ARGS(data, flags) \ (data)->pn ? (void *)(data)->pn : (void *)(data)->ts, \ ((data)->pn ? JSREPORT_PN : JSREPORT_TS) | (flags) static JSBool BumpFormalCount(JSContext *cx, JSFunction *fun) { if (fun->nargs == JS_BITMASK(16)) { JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_TOO_MANY_FUN_ARGS); return JS_FALSE; } fun->nargs++; return JS_TRUE; } static JSBool BindArg(JSContext *cx, BindData *data, JSAtom *atom, JSTreeContext *tc) { JSObject *obj, *pobj; JSProperty *prop; JSBool ok; uintN dupflag; JSFunction *fun; const char *name; obj = data->obj; ok = js_LookupHiddenProperty(cx, obj, ATOM_TO_JSID(atom), &pobj, &prop); if (!ok) return JS_FALSE; dupflag = 0; if (prop) { JS_ASSERT(pobj == obj); name = js_AtomToPrintableString(cx, atom); /* * A duplicate parameter name, a "feature" required by ECMA-262. * We force a duplicate node on the SCOPE_LAST_PROP(scope) list * with the same id, distinguished by the SPROP_IS_DUPLICATE flag, * and not mapped by an entry in scope. */ ok = name && js_ReportCompileErrorNumber(cx, BIND_DATA_REPORT_ARGS(data, JSREPORT_WARNING | JSREPORT_STRICT), JSMSG_DUPLICATE_FORMAL, name); OBJ_DROP_PROPERTY(cx, pobj, prop); if (!ok) return JS_FALSE; dupflag = SPROP_IS_DUPLICATE; } fun = data->u.arg.fun; if (!js_AddHiddenProperty(cx, data->obj, ATOM_TO_JSID(atom), js_GetArgument, js_SetArgument, SPROP_INVALID_SLOT, JSPROP_PERMANENT | JSPROP_SHARED, dupflag | SPROP_HAS_SHORTID, fun->nargs)) { return JS_FALSE; } return BumpFormalCount(cx, fun); } static JSBool BindLocalVariable(JSContext *cx, BindData *data, JSAtom *atom) { JSFunction *fun; /* * Can't increase fun->nvars in an active frame, so insist that getter is * js_GetLocalVariable, not js_GetCallVariable or anything else. */ if (data->u.var.getter != js_GetLocalVariable) return JS_TRUE; /* * Don't bind a variable with the hidden name 'arguments', per ECMA-262. * Instead 'var arguments' always restates the predefined property of the * activation objects with unhidden name 'arguments'. Assignment to such * a variable must be handled specially. */ if (atom == cx->runtime->atomState.argumentsAtom) return JS_TRUE; fun = data->u.var.fun; if (!js_AddHiddenProperty(cx, data->obj, ATOM_TO_JSID(atom), data->u.var.getter, data->u.var.setter, SPROP_INVALID_SLOT, data->u.var.attrs | JSPROP_SHARED, SPROP_HAS_SHORTID, fun->u.i.nvars)) { return JS_FALSE; } if (fun->u.i.nvars == JS_BITMASK(16)) { JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_TOO_MANY_FUN_VARS); return JS_FALSE; } fun->u.i.nvars++; return JS_TRUE; } #if JS_HAS_DESTRUCTURING /* * Forward declaration to maintain top-down presentation. */ static JSParseNode * DestructuringExpr(JSContext *cx, BindData *data, JSTreeContext *tc, JSTokenType tt); static JSBool BindDestructuringArg(JSContext *cx, BindData *data, JSAtom *atom, JSTreeContext *tc) { JSAtomListElement *ale; JSFunction *fun; JSObject *obj, *pobj; JSProperty *prop; const char *name; ATOM_LIST_SEARCH(ale, &tc->decls, atom); if (!ale) { ale = js_IndexAtom(cx, atom, &tc->decls); if (!ale) return JS_FALSE; ALE_SET_JSOP(ale, data->op); } fun = data->u.var.fun; obj = data->obj; if (!js_LookupHiddenProperty(cx, obj, ATOM_TO_JSID(atom), &pobj, &prop)) return JS_FALSE; if (prop) { JS_ASSERT(pobj == obj && OBJ_IS_NATIVE(pobj)); name = js_AtomToPrintableString(cx, atom); if (!name || !js_ReportCompileErrorNumber(cx, BIND_DATA_REPORT_ARGS(data, JSREPORT_WARNING | JSREPORT_STRICT), JSMSG_DUPLICATE_FORMAL, name)) { return JS_FALSE; } OBJ_DROP_PROPERTY(cx, pobj, prop); } else { if (!BindLocalVariable(cx, data, atom)) return JS_FALSE; } return JS_TRUE; } #endif /* JS_HAS_DESTRUCTURING */ static JSParseNode * FunctionDef(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc, JSBool lambda) { JSOp op, prevop; JSParseNode *pn, *body, *result; JSTokenType tt; JSAtom *funAtom, *objAtom; JSStackFrame *fp; JSObject *varobj, *pobj; JSAtomListElement *ale; JSProperty *prop; JSFunction *fun; JSTreeContext funtc; #if JS_HAS_DESTRUCTURING JSParseNode *item, *list = NULL; #endif /* Make a TOK_FUNCTION node. */ #if JS_HAS_GETTER_SETTER op = CURRENT_TOKEN(ts).t_op; #endif pn = NewParseNode(cx, ts, PN_FUNC, tc); if (!pn) return NULL; /* Scan the optional function name into funAtom. */ ts->flags |= TSF_KEYWORD_IS_NAME; tt = js_GetToken(cx, ts); ts->flags &= ~TSF_KEYWORD_IS_NAME; if (tt == TOK_NAME) { funAtom = CURRENT_TOKEN(ts).t_atom; } else { funAtom = NULL; js_UngetToken(ts); } /* Find the nearest variable-declaring scope and use it as our parent. */ fp = cx->fp; varobj = fp->varobj; /* * Record names for function statements in tc->decls so we know when to * avoid optimizing variable references that might name a function. */ if (!lambda && funAtom) { ATOM_LIST_SEARCH(ale, &tc->decls, funAtom); if (ale) { prevop = ALE_JSOP(ale); if (JS_HAS_STRICT_OPTION(cx) || prevop == JSOP_DEFCONST) { const char *name = js_AtomToPrintableString(cx, funAtom); if (!name || !js_ReportCompileErrorNumber(cx, ts, (prevop != JSOP_DEFCONST) ? JSREPORT_TS | JSREPORT_WARNING | JSREPORT_STRICT : JSREPORT_TS | JSREPORT_ERROR, JSMSG_REDECLARED_VAR, (prevop == JSOP_DEFFUN || prevop == JSOP_CLOSURE) ? js_function_str : (prevop == JSOP_DEFCONST) ? js_const_str : js_var_str, name)) { return NULL; } } if (!AT_TOP_LEVEL(tc) && prevop == JSOP_DEFVAR) tc->flags |= TCF_FUN_CLOSURE_VS_VAR; } else { ale = js_IndexAtom(cx, funAtom, &tc->decls); if (!ale) return NULL; } ALE_SET_JSOP(ale, AT_TOP_LEVEL(tc) ? JSOP_DEFFUN : JSOP_CLOSURE); /* * A function nested at top level inside another's body needs only a * local variable to bind its name to its value, and not an activation * object property (it might also need the activation property, if the * outer function contains with statements, e.g., but the stack slot * wins when jsemit.c's BindNameToSlot can optimize a JSOP_NAME into a * JSOP_GETVAR bytecode). */ if (AT_TOP_LEVEL(tc) && (tc->flags & TCF_IN_FUNCTION)) { JSScopeProperty *sprop; /* * Define a property on the outer function so that BindNameToSlot * can properly optimize accesses. */ JS_ASSERT(OBJ_GET_CLASS(cx, varobj) == &js_FunctionClass); JS_ASSERT(fp->fun == (JSFunction *) JS_GetPrivate(cx, varobj)); if (!js_LookupHiddenProperty(cx, varobj, ATOM_TO_JSID(funAtom), &pobj, &prop)) { return NULL; } if (prop) OBJ_DROP_PROPERTY(cx, pobj, prop); sprop = NULL; if (!prop || pobj != varobj || (sprop = (JSScopeProperty *)prop, sprop->getter != js_GetLocalVariable)) { uintN sflags; /* * Use SPROP_IS_DUPLICATE if there is a formal argument of the * same name, so the decompiler can find the parameter name. */ sflags = (sprop && sprop->getter == js_GetArgument) ? SPROP_IS_DUPLICATE | SPROP_HAS_SHORTID : SPROP_HAS_SHORTID; if (!js_AddHiddenProperty(cx, varobj, ATOM_TO_JSID(funAtom), js_GetLocalVariable, js_SetLocalVariable, SPROP_INVALID_SLOT, JSPROP_PERMANENT | JSPROP_SHARED, sflags, fp->fun->u.i.nvars)) { return NULL; } if (fp->fun->u.i.nvars == JS_BITMASK(16)) { JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_TOO_MANY_FUN_VARS); return NULL; } fp->fun->u.i.nvars++; } } } fun = js_NewFunction(cx, NULL, NULL, 0, lambda ? JSFUN_LAMBDA : 0, varobj, funAtom); if (!fun) return NULL; #if JS_HAS_GETTER_SETTER if (op != JSOP_NOP) fun->flags |= (op == JSOP_GETTER) ? JSPROP_GETTER : JSPROP_SETTER; #endif /* * Atomize fun->object early to protect against a last-ditch GC under * js_LookupHiddenProperty. * * Absent use of the new scoped local GC roots API around compiler calls, * we need to atomize here to protect against a GC activation. Atoms are * protected from GC during compilation by the JS_FRIEND_API entry points * in this file. There doesn't seem to be any gain in switching from the * atom-keeping method to the bulkier, slower scoped local roots method. */ objAtom = js_AtomizeObject(cx, fun->object, 0); if (!objAtom) return NULL; /* Initialize early for possible flags mutation via DestructuringExpr. */ TREE_CONTEXT_INIT(&funtc); /* Now parse formal argument list and compute fun->nargs. */ MUST_MATCH_TOKEN(TOK_LP, JSMSG_PAREN_BEFORE_FORMAL); if (!js_MatchToken(cx, ts, TOK_RP)) { BindData data; data.pn = NULL; data.ts = ts; data.obj = fun->object; data.op = JSOP_NOP; data.binder = BindArg; data.u.arg.fun = fun; do { tt = js_GetToken(cx, ts); switch (tt) { #if JS_HAS_DESTRUCTURING case TOK_LB: case TOK_LC: { JSParseNode *lhs, *rhs; jsint slot; /* * A destructuring formal parameter turns into one or more * local variables initialized from properties of a single * anonymous positional parameter, so here we must tweak our * binder and its data. */ data.op = JSOP_DEFVAR; data.binder = BindDestructuringArg; data.u.var.clasp = &js_FunctionClass; data.u.var.getter = js_GetLocalVariable; data.u.var.setter = js_SetLocalVariable; data.u.var.attrs = JSPROP_PERMANENT; /* * Temporarily transfer the owneship of the recycle list to * funtc. See bug 313967. */ funtc.nodeList = tc->nodeList; tc->nodeList = NULL; lhs = DestructuringExpr(cx, &data, &funtc, tt); tc->nodeList = funtc.nodeList; funtc.nodeList = NULL; if (!lhs) return NULL; /* * Restore the formal parameter binder in case there are more * non-destructuring formals in the parameter list. */ data.binder = BindArg; /* * Adjust fun->nargs to count the single anonymous positional * parameter that is to be destructured. */ slot = fun->nargs; if (!BumpFormalCount(cx, fun)) return NULL; /* * Synthesize a destructuring assignment from the single * anonymous positional parameter into the destructuring * left-hand-side expression and accumulate it in list. */ rhs = NewParseNode(cx, ts, PN_NAME, tc); if (!rhs) return NULL; rhs->pn_type = TOK_NAME; rhs->pn_op = JSOP_GETARG; rhs->pn_atom = cx->runtime->atomState.emptyAtom; rhs->pn_expr = NULL; rhs->pn_slot = slot; rhs->pn_attrs = 0; item = NewBinary(cx, TOK_ASSIGN, JSOP_NOP, lhs, rhs, tc); if (!item) return NULL; if (!list) { list = NewParseNode(cx, ts, PN_LIST, tc); if (!list) return NULL; list->pn_type = TOK_COMMA; PN_INIT_LIST(list); } PN_APPEND(list, item); break; } #endif /* JS_HAS_DESTRUCTURING */ case TOK_NAME: if (!data.binder(cx, &data, CURRENT_TOKEN(ts).t_atom, tc)) return NULL; break; default: js_ReportCompileErrorNumber(cx, ts, JSREPORT_TS | JSREPORT_ERROR, JSMSG_MISSING_FORMAL); return NULL; } } while (js_MatchToken(cx, ts, TOK_COMMA)); MUST_MATCH_TOKEN(TOK_RP, JSMSG_PAREN_AFTER_FORMAL); } MUST_MATCH_TOKEN(TOK_LC, JSMSG_CURLY_BEFORE_BODY); pn->pn_pos.begin = CURRENT_TOKEN(ts).pos.begin; /* * Temporarily transfer the owneship of the recycle list to funtc. * See bug 313967. */ funtc.nodeList = tc->nodeList; tc->nodeList = NULL; body = FunctionBody(cx, ts, fun, &funtc); tc->nodeList = funtc.nodeList; funtc.nodeList = NULL; if (!body) return NULL; MUST_MATCH_TOKEN(TOK_RC, JSMSG_CURLY_AFTER_BODY); pn->pn_pos.end = CURRENT_TOKEN(ts).pos.end; #if JS_HAS_DESTRUCTURING /* * If there were destructuring formal parameters, prepend the initializing * comma expression that we synthesized to body. If the body is a lexical * scope node, we must make a special TOK_BODY node, to prepend the formal * parameter destructuring code without bracing the decompilation of the * function body's lexical scope. */ if (list) { if (body->pn_arity != PN_LIST) { JSParseNode *block; JS_ASSERT(body->pn_type == TOK_LEXICALSCOPE); JS_ASSERT(body->pn_arity == PN_NAME); block = NewParseNode(cx, ts, PN_LIST, tc); if (!block) return NULL; block->pn_type = TOK_BODY; block->pn_pos = body->pn_pos; PN_INIT_LIST_1(block, body); body = block; } item = NewParseNode(cx, ts, PN_UNARY, tc); if (!item) return NULL; item->pn_type = TOK_SEMI; item->pn_pos.begin = item->pn_pos.end = body->pn_pos.begin; item->pn_kid = list; item->pn_next = body->pn_head; body->pn_head = item; if (body->pn_tail == &body->pn_head) body->pn_tail = &item->pn_next; ++body->pn_count; } #endif /* * If we collected flags that indicate nested heavyweight functions, or * this function contains heavyweight-making statements (references to * __parent__ or __proto__; use of with, eval, import, or export; and * assignment to arguments), flag the function as heavyweight (requiring * a call object per invocation). */ if (funtc.flags & TCF_FUN_HEAVYWEIGHT) { fun->flags |= JSFUN_HEAVYWEIGHT; tc->flags |= TCF_FUN_HEAVYWEIGHT; } else { /* * If this function is a named statement function not at top-level * (i.e. a JSOP_CLOSURE, not a function definiton or expression), then * our enclosing function, if any, must be heavyweight. * * The TCF_FUN_USES_NONLOCALS flag is set only by the code generator, * so it won't be set here. Assert that it's not. We have to check * it later, in js_EmitTree, after js_EmitFunctionBody has traversed * the function's body */ JS_ASSERT(!(funtc.flags & TCF_FUN_USES_NONLOCALS)); if (!lambda && funAtom && !AT_TOP_LEVEL(tc)) tc->flags |= TCF_FUN_HEAVYWEIGHT; } result = pn; if (lambda) { /* * ECMA ed. 3 standard: function expression, possibly anonymous. */ op = funAtom ? JSOP_NAMEDFUNOBJ : JSOP_ANONFUNOBJ; } else if (!funAtom) { /* * If this anonymous function definition is *not* embedded within a * larger expression, we treat it as an expression statement, not as * a function declaration -- and not as a syntax error (as ECMA-262 * Edition 3 would have it). Backward compatibility trumps all. */ result = NewParseNode(cx, ts, PN_UNARY, tc); if (!result) return NULL; result->pn_type = TOK_SEMI; result->pn_pos = pn->pn_pos; result->pn_kid = pn; op = JSOP_ANONFUNOBJ; } else if (!AT_TOP_LEVEL(tc)) { /* * ECMA ed. 3 extension: a function expression statement not at the * top level, e.g., in a compound statement such as the "then" part * of an "if" statement, binds a closure only if control reaches that * sub-statement. */ op = JSOP_CLOSURE; } else { op = JSOP_NOP; } pn->pn_funAtom = objAtom; pn->pn_op = op; pn->pn_body = body; pn->pn_flags = funtc.flags & (TCF_FUN_FLAGS | TCF_HAS_DEFXMLNS); pn->pn_tryCount = funtc.tryCount; TREE_CONTEXT_FINISH(&funtc); return result; } static JSParseNode * FunctionStmt(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc) { return FunctionDef(cx, ts, tc, JS_FALSE); } static JSParseNode * FunctionExpr(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc) { return FunctionDef(cx, ts, tc, JS_TRUE); } /* * Parse the statements in a block, creating a TOK_LC node that lists the * statements' trees. If called from block-parsing code, the caller must * match { before and } after. */ static JSParseNode * Statements(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc) { JSParseNode *pn, *pn2, *saveBlock; JSTokenType tt; CHECK_RECURSION(); pn = NewParseNode(cx, ts, PN_LIST, tc); if (!pn) return NULL; saveBlock = tc->blockNode; tc->blockNode = pn; PN_INIT_LIST(pn); ts->flags |= TSF_OPERAND; while ((tt = js_PeekToken(cx, ts)) > TOK_EOF && tt != TOK_RC) { ts->flags &= ~TSF_OPERAND; pn2 = Statement(cx, ts, tc); if (!pn2) { if (ts->flags & TSF_EOF) ts->flags |= TSF_UNEXPECTED_EOF; return NULL; } ts->flags |= TSF_OPERAND; /* Detect a function statement for the TOK_LC case in Statement. */ if (pn2->pn_type == TOK_FUNCTION && !AT_TOP_LEVEL(tc)) tc->flags |= TCF_HAS_FUNCTION_STMT; /* If compiling top-level statements, emit as we go to save space. */ if (!tc->topStmt && (tc->flags & TCF_COMPILING)) { if (cx->fp->fun && JS_HAS_STRICT_OPTION(cx) && (tc->flags & TCF_RETURN_EXPR)) { /* * Check pn2 for lack of a final return statement if it is the * last statement in the block. */ tt = js_PeekToken(cx, ts); if ((tt == TOK_EOF || tt == TOK_RC) && !CheckFinalReturn(cx, ts, pn2)) { tt = TOK_ERROR; break; } /* * Clear TCF_RETURN_EXPR so FunctionBody doesn't try to * CheckFinalReturn again. */ tc->flags &= ~TCF_RETURN_EXPR; } if (!js_FoldConstants(cx, pn2, tc) || !js_AllocTryNotes(cx, (JSCodeGenerator *)tc) || !js_EmitTree(cx, (JSCodeGenerator *)tc, pn2)) { tt = TOK_ERROR; break; } RecycleTree(pn2, tc); } else { PN_APPEND(pn, pn2); } } /* * Handle the case where there was a let declaration under this block. If * it replaced tc->blockNode with a new block node then we must refresh pn * and then restore tc->blockNode. */ if (tc->blockNode != pn) pn = tc->blockNode; tc->blockNode = saveBlock; ts->flags &= ~TSF_OPERAND; if (tt == TOK_ERROR) return NULL; pn->pn_pos.end = CURRENT_TOKEN(ts).pos.end; return pn; } static JSParseNode * Condition(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc) { JSParseNode *pn, *pn2; MUST_MATCH_TOKEN(TOK_LP, JSMSG_PAREN_BEFORE_COND); pn = Expr(cx, ts, tc); if (!pn) return NULL; MUST_MATCH_TOKEN(TOK_RP, JSMSG_PAREN_AFTER_COND); /* * Check for (a = b) and "correct" it to (a == b) iff b's operator has * greater precedence than ==. * XXX not ECMA, but documented in several books -- now a strict warning. */ if (pn->pn_type == TOK_ASSIGN && pn->pn_op == JSOP_NOP && pn->pn_right->pn_type > TOK_EQOP) { JSBool rewrite = !JS_VERSION_IS_ECMA(cx); if (!js_ReportCompileErrorNumber(cx, ts, JSREPORT_TS | JSREPORT_WARNING | JSREPORT_STRICT, JSMSG_EQUAL_AS_ASSIGN, rewrite ? "\nAssuming equality test" : "")) { return NULL; } if (rewrite) { pn->pn_type = TOK_EQOP; pn->pn_op = (JSOp)cx->jsop_eq; pn2 = pn->pn_left; switch (pn2->pn_op) { case JSOP_SETNAME: pn2->pn_op = JSOP_NAME; break; case JSOP_SETPROP: pn2->pn_op = JSOP_GETPROP; break; case JSOP_SETELEM: pn2->pn_op = JSOP_GETELEM; break; default: JS_ASSERT(0); } } } return pn; } static JSBool MatchLabel(JSContext *cx, JSTokenStream *ts, JSParseNode *pn) { JSAtom *label; JSTokenType tt; tt = js_PeekTokenSameLine(cx, ts); if (tt == TOK_ERROR) return JS_FALSE; if (tt == TOK_NAME) { (void) js_GetToken(cx, ts); label = CURRENT_TOKEN(ts).t_atom; } else { label = NULL; } pn->pn_atom = label; return JS_TRUE; } #if JS_HAS_EXPORT_IMPORT static JSParseNode * ImportExpr(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc) { JSParseNode *pn, *pn2; JSTokenType tt; MUST_MATCH_TOKEN(TOK_NAME, JSMSG_NO_IMPORT_NAME); pn = NewParseNode(cx, ts, PN_NAME, tc); if (!pn) return NULL; pn->pn_op = JSOP_NAME; pn->pn_atom = CURRENT_TOKEN(ts).t_atom; pn->pn_expr = NULL; pn->pn_slot = -1; pn->pn_attrs = 0; ts->flags |= TSF_OPERAND; while ((tt = js_GetToken(cx, ts)) == TOK_DOT || tt == TOK_LB) { ts->flags &= ~TSF_OPERAND; if (pn->pn_op == JSOP_IMPORTALL) goto bad_import; if (tt == TOK_DOT) { pn2 = NewParseNode(cx, ts, PN_NAME, tc); if (!pn2) return NULL; ts->flags |= TSF_KEYWORD_IS_NAME; if (js_MatchToken(cx, ts, TOK_STAR)) { pn2->pn_op = JSOP_IMPORTALL; pn2->pn_atom = NULL; pn2->pn_slot = -1; pn2->pn_attrs = 0; } else { MUST_MATCH_TOKEN(TOK_NAME, JSMSG_NAME_AFTER_DOT); pn2->pn_op = JSOP_GETPROP; pn2->pn_atom = CURRENT_TOKEN(ts).t_atom; pn2->pn_slot = -1; pn2->pn_attrs = 0; } ts->flags &= ~TSF_KEYWORD_IS_NAME; pn2->pn_expr = pn; pn2->pn_pos.begin = pn->pn_pos.begin; pn2->pn_pos.end = CURRENT_TOKEN(ts).pos.end; } else { /* Make a TOK_LB binary node. */ pn2 = NewBinary(cx, tt, JSOP_GETELEM, pn, Expr(cx, ts, tc), tc); if (!pn2) return NULL; MUST_MATCH_TOKEN(TOK_RB, JSMSG_BRACKET_IN_INDEX); } pn = pn2; ts->flags |= TSF_OPERAND; } ts->flags &= ~TSF_OPERAND; if (tt == TOK_ERROR) return NULL; js_UngetToken(ts); switch (pn->pn_op) { case JSOP_GETPROP: pn->pn_op = JSOP_IMPORTPROP; break; case JSOP_GETELEM: pn->pn_op = JSOP_IMPORTELEM; break; case JSOP_IMPORTALL: break; default: goto bad_import; } return pn; bad_import: js_ReportCompileErrorNumber(cx, ts, JSREPORT_TS | JSREPORT_ERROR, JSMSG_BAD_IMPORT); return NULL; } #endif /* JS_HAS_EXPORT_IMPORT */ static JSBool BindLet(JSContext *cx, BindData *data, JSAtom *atom, JSTreeContext *tc) { JSObject *blockObj; JSScopeProperty *sprop; JSAtomListElement *ale; blockObj = data->obj; sprop = SCOPE_GET_PROPERTY(OBJ_SCOPE(blockObj), ATOM_TO_JSID(atom)); ATOM_LIST_SEARCH(ale, &tc->decls, atom); if (sprop || (ale && ALE_JSOP(ale) == JSOP_DEFCONST)) { const char *name; if (sprop) { JS_ASSERT(sprop->flags & SPROP_HAS_SHORTID); JS_ASSERT((uint16)sprop->shortid < data->u.let.index); } name = js_AtomToPrintableString(cx, atom); if (name) { js_ReportCompileErrorNumber(cx, BIND_DATA_REPORT_ARGS(data, JSREPORT_ERROR), JSMSG_REDECLARED_VAR, (ale && ALE_JSOP(ale) == JSOP_DEFCONST) ? js_const_str : "variable", name); } return JS_FALSE; } if (data->u.let.index == JS_BIT(16)) { js_ReportCompileErrorNumber(cx, BIND_DATA_REPORT_ARGS(data, JSREPORT_ERROR), data->u.let.overflow); return JS_FALSE; } /* Use JSPROP_ENUMERATE to aid the disassembler. */ return js_DefineNativeProperty(cx, blockObj, ATOM_TO_JSID(atom), JSVAL_VOID, NULL, NULL, JSPROP_ENUMERATE | JSPROP_PERMANENT, SPROP_HAS_SHORTID, (intN)data->u.let.index++, NULL); } static JSBool BindVarOrConst(JSContext *cx, BindData *data, JSAtom *atom, JSTreeContext *tc) { JSStmtInfo *stmt; JSAtomListElement *ale; JSOp op, prevop; const char *name; JSFunction *fun; JSObject *obj, *pobj; JSProperty *prop; JSBool ok; JSPropertyOp getter, setter; JSScopeProperty *sprop; stmt = js_LexicalLookup(tc, atom, NULL, JS_FALSE); ATOM_LIST_SEARCH(ale, &tc->decls, atom); op = data->op; if ((stmt && stmt->type != STMT_WITH) || ale) { prevop = ale ? ALE_JSOP(ale) : JSOP_DEFVAR; if (JS_HAS_STRICT_OPTION(cx) ? op != JSOP_DEFVAR || prevop != JSOP_DEFVAR : op == JSOP_DEFCONST || prevop == JSOP_DEFCONST) { name = js_AtomToPrintableString(cx, atom); if (!name || !js_ReportCompileErrorNumber(cx, BIND_DATA_REPORT_ARGS(data, (op != JSOP_DEFCONST && prevop != JSOP_DEFCONST) ? JSREPORT_WARNING | JSREPORT_STRICT : JSREPORT_ERROR), JSMSG_REDECLARED_VAR, (prevop == JSOP_DEFFUN || prevop == JSOP_CLOSURE) ? js_function_str : (prevop == JSOP_DEFCONST) ? js_const_str : js_var_str, name)) { return JS_FALSE; } } if (op == JSOP_DEFVAR && prevop == JSOP_CLOSURE) tc->flags |= TCF_FUN_CLOSURE_VS_VAR; } if (!ale) { ale = js_IndexAtom(cx, atom, &tc->decls); if (!ale) return JS_FALSE; } ALE_SET_JSOP(ale, op); fun = data->u.var.fun; obj = data->obj; if (!fun) { /* Don't lookup global variables at compile time. */ prop = NULL; } else { JS_ASSERT(OBJ_IS_NATIVE(obj)); if (!js_LookupHiddenProperty(cx, obj, ATOM_TO_JSID(atom), &pobj, &prop)) { return JS_FALSE; } } ok = JS_TRUE; getter = data->u.var.getter; setter = data->u.var.setter; if (prop && pobj == obj && OBJ_IS_NATIVE(pobj)) { sprop = (JSScopeProperty *)prop; if (sprop->getter == js_GetArgument) { name = js_AtomToPrintableString(cx, atom); if (!name) { ok = JS_FALSE; } else if (op == JSOP_DEFCONST) { js_ReportCompileErrorNumber(cx, BIND_DATA_REPORT_ARGS(data, JSREPORT_ERROR), JSMSG_REDECLARED_PARAM, name); ok = JS_FALSE; } else { getter = js_GetArgument; setter = js_SetArgument; ok = js_ReportCompileErrorNumber(cx, BIND_DATA_REPORT_ARGS(data, JSREPORT_WARNING | JSREPORT_STRICT), JSMSG_VAR_HIDES_ARG, name); } } else { JS_ASSERT(getter == js_GetLocalVariable); if (fun) { /* Not an argument, must be a redeclared local var. */ if (data->u.var.clasp == &js_FunctionClass) { JS_ASSERT(sprop->getter == js_GetLocalVariable); JS_ASSERT((sprop->flags & SPROP_HAS_SHORTID) && (uint16) sprop->shortid < fun->u.i.nvars); } else if (data->u.var.clasp == &js_CallClass) { if (sprop->getter == js_GetCallVariable) { /* * Referencing a name introduced by a var statement in * the enclosing function. Check that the slot number * we have is in range. */ JS_ASSERT((sprop->flags & SPROP_HAS_SHORTID) && (uint16) sprop->shortid < fun->u.i.nvars); } else { /* * A variable introduced through another eval: don't * use the special getters and setters since we can't * allocate a slot in the frame. */ getter = sprop->getter; setter = sprop->setter; } } /* Override the old getter and setter, to handle eval. */ sprop = js_ChangeNativePropertyAttrs(cx, obj, sprop, 0, sprop->attrs, getter, setter); if (!sprop) ok = JS_FALSE; } } if (prop) OBJ_DROP_PROPERTY(cx, pobj, prop); } else { /* * Property not found in current variable scope: we have not seen this * variable before. Define a new local variable by adding a property * to the function's scope, allocating one slot in the function's vars * frame. Global variables and any locals declared in with statement * bodies are handled at runtime, by script prolog JSOP_DEFVAR opcodes * generated for slot-less vars. */ sprop = NULL; if (prop) { OBJ_DROP_PROPERTY(cx, pobj, prop); prop = NULL; } if (cx->fp->scopeChain == obj && !js_InWithStatement(tc) && !BindLocalVariable(cx, data, atom)) { return JS_FALSE; } } return ok; } #if JS_HAS_DESTRUCTURING static JSBool BindDestructuringVar(JSContext *cx, BindData *data, JSParseNode *pn, JSTreeContext *tc) { JSAtom *atom; /* * Destructuring is a form of assignment, so just as for an initialized * simple variable, we must check for assignment to 'arguments' and flag * the enclosing function (if any) as heavyweight. */ JS_ASSERT(pn->pn_type == TOK_NAME); atom = pn->pn_atom; if (atom == cx->runtime->atomState.argumentsAtom) tc->flags |= TCF_FUN_HEAVYWEIGHT; data->pn = pn; if (!data->binder(cx, data, atom, tc)) return JS_FALSE; data->pn = NULL; /* * Select the appropriate name-setting opcode, which may be specialized * further for local variable and argument slot optimizations. At this * point, we can't select the optimal final opcode, yet we must preserve * the CONST bit and convey "set", not "get". */ pn->pn_op = (data->op == JSOP_DEFCONST) ? JSOP_SETCONST : JSOP_SETNAME; pn->pn_attrs = data->u.var.attrs; return JS_TRUE; } /* * Here, we are destructuring {... P: Q, ...} = R, where P is any id, Q is any * LHS expression except a destructuring initialiser, and R is on the stack. * Because R is already evaluated, the usual LHS-specialized bytecodes won't * work. After pushing R[P] we need to evaluate Q's "reference base" QB and * then push its property name QN. At this point the stack looks like * * [... R, R[P], QB, QN] * * We need to set QB[QN] = R[P]. This is a job for JSOP_ENUMELEM, which takes * its operands with left-hand side above right-hand side: * * [rval, lval, xval] * * and pops all three values, setting lval[xval] = rval. But we cannot select * JSOP_ENUMELEM yet, because the LHS may turn out to be an arg or local var, * which can be optimized further. So we select JSOP_SETNAME. */ static JSBool BindDestructuringLHS(JSContext *cx, JSParseNode *pn, JSTreeContext *tc) { while (pn->pn_type == TOK_RP) pn = pn->pn_kid; switch (pn->pn_type) { case TOK_NAME: if (pn->pn_atom == cx->runtime->atomState.argumentsAtom) tc->flags |= TCF_FUN_HEAVYWEIGHT; /* FALL THROUGH */ case TOK_DOT: case TOK_LB: pn->pn_op = JSOP_SETNAME; break; #if JS_HAS_LVALUE_RETURN case TOK_LP: JS_ASSERT(pn->pn_op == JSOP_CALL || pn->pn_op == JSOP_EVAL); pn->pn_op = JSOP_SETCALL; break; #endif #if JS_HAS_XML_SUPPORT case TOK_UNARYOP: if (pn->pn_op == JSOP_XMLNAME) { pn->pn_op = JSOP_BINDXMLNAME; break; } /* FALL THROUGH */ #endif default: js_ReportCompileErrorNumber(cx, pn, JSREPORT_PN | JSREPORT_ERROR, JSMSG_BAD_LEFTSIDE_OF_ASS); return JS_FALSE; } return JS_TRUE; } typedef struct FindPropValData { uint32 numvars; /* # of destructuring vars in left side */ uint32 maxstep; /* max # of steps searching right side */ JSDHashTable table; /* hash table for O(1) right side search */ } FindPropValData; typedef struct FindPropValEntry { JSDHashEntryHdr hdr; JSParseNode *pnkey; JSParseNode *pnval; } FindPropValEntry; #define ASSERT_VALID_PROPERTY_KEY(pnkey) \ JS_ASSERT((pnkey)->pn_arity == PN_NULLARY && \ ((pnkey)->pn_type == TOK_NUMBER || \ (pnkey)->pn_type == TOK_STRING || \ (pnkey)->pn_type == TOK_NAME)) JS_STATIC_DLL_CALLBACK(JSDHashNumber) HashFindPropValKey(JSDHashTable *table, const void *key) { const JSParseNode *pnkey = (const JSParseNode *)key; ASSERT_VALID_PROPERTY_KEY(pnkey); return (pnkey->pn_type == TOK_NUMBER) ? (JSDHashNumber) (JSDOUBLE_HI32(pnkey->pn_dval) ^ JSDOUBLE_LO32(pnkey->pn_dval)) : (JSDHashNumber) pnkey->pn_atom->number; } JS_STATIC_DLL_CALLBACK(JSBool) MatchFindPropValEntry(JSDHashTable *table, const JSDHashEntryHdr *entry, const void *key) { const FindPropValEntry *fpve = (const FindPropValEntry *)entry; const JSParseNode *pnkey = (const JSParseNode *)key; ASSERT_VALID_PROPERTY_KEY(pnkey); return pnkey->pn_type == fpve->pnkey->pn_type && ((pnkey->pn_type == TOK_NUMBER) ? pnkey->pn_dval == fpve->pnkey->pn_dval : pnkey->pn_atom == fpve->pnkey->pn_atom); } static const JSDHashTableOps FindPropValOps = { JS_DHashAllocTable, JS_DHashFreeTable, JS_DHashGetKeyStub, HashFindPropValKey, MatchFindPropValEntry, JS_DHashMoveEntryStub, JS_DHashClearEntryStub, JS_DHashFinalizeStub, NULL }; #define STEP_HASH_THRESHOLD 10 #define BIG_DESTRUCTURING 5 #define BIG_OBJECT_INIT 20 static JSParseNode * FindPropertyValue(JSParseNode *pn, JSParseNode *pnid, FindPropValData *data) { FindPropValEntry *entry; JSParseNode *pnhit, *pnprop, *pnkey; uint32 step; /* If we have a hash table, use it as the sole source of truth. */ if (data->table.ops) { entry = (FindPropValEntry *) JS_DHashTableOperate(&data->table, pnid, JS_DHASH_LOOKUP); return JS_DHASH_ENTRY_IS_BUSY(&entry->hdr) ? entry->pnval : NULL; } /* If pn is not an object initialiser node, we can't do anything here. */ if (pn->pn_type != TOK_RC) return NULL; /* * We must search all the way through pn's list, to handle the case of an * id duplicated for two or more property initialisers. */ pnhit = NULL; step = 0; ASSERT_VALID_PROPERTY_KEY(pnid); if (pnid->pn_type == TOK_NUMBER) { for (pnprop = pn->pn_head; pnprop; pnprop = pnprop->pn_next) { JS_ASSERT(pnprop->pn_type == TOK_COLON); if (pnprop->pn_op == JSOP_NOP) { pnkey = pnprop->pn_left; ASSERT_VALID_PROPERTY_KEY(pnkey); if (pnkey->pn_type == TOK_NUMBER && pnkey->pn_dval == pnid->pn_dval) { pnhit = pnprop; } ++step; } } } else { for (pnprop = pn->pn_head; pnprop; pnprop = pnprop->pn_next) { JS_ASSERT(pnprop->pn_type == TOK_COLON); if (pnprop->pn_op == JSOP_NOP) { pnkey = pnprop->pn_left; ASSERT_VALID_PROPERTY_KEY(pnkey); if (pnkey->pn_type == pnid->pn_type && pnkey->pn_atom == pnid->pn_atom) { pnhit = pnprop; } ++step; } } } if (!pnhit) return NULL; /* Hit via full search -- see whether it's time to create the hash table. */ JS_ASSERT(!data->table.ops); if (step > data->maxstep) { data->maxstep = step; if (step >= STEP_HASH_THRESHOLD && data->numvars >= BIG_DESTRUCTURING && pn->pn_count >= BIG_OBJECT_INIT && JS_DHashTableInit(&data->table, &FindPropValOps, pn, sizeof(FindPropValEntry), pn->pn_count)) { for (pn = pn->pn_head; pn; pn = pn->pn_next) { ASSERT_VALID_PROPERTY_KEY(pn->pn_left); entry = (FindPropValEntry *) JS_DHashTableOperate(&data->table, pn->pn_left, JS_DHASH_ADD); entry->pnval = pn->pn_right; } } } return pnhit->pn_right; } /* * If data is null, the caller is AssignExpr and instead of binding variables, * we specialize lvalues in the propery value positions of the left-hand side. * If right is null, just check for well-formed lvalues. */ static JSBool CheckDestructuring(JSContext *cx, BindData *data, JSParseNode *left, JSParseNode *right, JSTreeContext *tc) { JSBool ok; FindPropValData fpvd; JSParseNode *lhs, *rhs, *pn, *pn2; if (left->pn_type == TOK_ARRAYCOMP) { js_ReportCompileErrorNumber(cx, left, JSREPORT_PN | JSREPORT_ERROR, JSMSG_ARRAY_COMP_LEFTSIDE); return JS_FALSE; } ok = JS_TRUE; fpvd.table.ops = NULL; lhs = left->pn_head; if (lhs && lhs->pn_type == TOK_DEFSHARP) { pn = lhs; goto no_var_name; } if (left->pn_type == TOK_RB) { rhs = (right && right->pn_type == left->pn_type) ? right->pn_head : NULL; while (lhs) { pn = lhs, pn2 = rhs; if (!data) { /* Skip parenthesization if not in a variable declaration. */ while (pn->pn_type == TOK_RP) pn = pn->pn_kid; if (pn2) { while (pn2->pn_type == TOK_RP) pn2 = pn2->pn_kid; } } /* Nullary comma is an elision; binary comma is an expression.*/ if (pn->pn_type != TOK_COMMA || pn->pn_arity != PN_NULLARY) { if (pn->pn_type == TOK_RB || pn->pn_type == TOK_RC) { ok = CheckDestructuring(cx, data, pn, pn2, tc); } else { if (data) { if (pn->pn_type != TOK_NAME) goto no_var_name; ok = BindDestructuringVar(cx, data, pn, tc); } else { ok = BindDestructuringLHS(cx, pn, tc); } } if (!ok) goto out; } lhs = lhs->pn_next; if (rhs) rhs = rhs->pn_next; } } else { JS_ASSERT(left->pn_type == TOK_RC); fpvd.numvars = left->pn_count; fpvd.maxstep = 0; rhs = NULL; while (lhs) { JS_ASSERT(lhs->pn_type == TOK_COLON); pn = lhs->pn_right; if (!data) { /* Skip parenthesization if not in a variable declaration. */ while (pn->pn_type == TOK_RP) pn = pn->pn_kid; } if (pn->pn_type == TOK_RB || pn->pn_type == TOK_RC) { if (right) { rhs = FindPropertyValue(right, lhs->pn_left, &fpvd); if (rhs && !data) { while (rhs->pn_type == TOK_RP) rhs = rhs->pn_kid; } } ok = CheckDestructuring(cx, data, pn, rhs, tc); } else if (data) { if (pn->pn_type != TOK_NAME) goto no_var_name; ok = BindDestructuringVar(cx, data, pn, tc); } else { ok = BindDestructuringLHS(cx, pn, tc); } if (!ok) goto out; lhs = lhs->pn_next; } } out: if (fpvd.table.ops) JS_DHashTableFinish(&fpvd.table); return ok; no_var_name: js_ReportCompileErrorNumber(cx, pn, JSREPORT_PN | JSREPORT_ERROR, JSMSG_NO_VARIABLE_NAME); ok = JS_FALSE; goto out; } static JSParseNode * DestructuringExpr(JSContext *cx, BindData *data, JSTreeContext *tc, JSTokenType tt) { JSParseNode *pn; pn = PrimaryExpr(cx, data->ts, tc, tt, JS_FALSE); if (!pn) return NULL; if (!CheckDestructuring(cx, data, pn, NULL, tc)) return NULL; return pn; } #endif /* JS_HAS_DESTRUCTURING */ extern const char js_with_statement_str[]; static JSParseNode * ContainsStmt(JSParseNode *pn, JSTokenType tt) { JSParseNode *pn2, *pnt; if (!pn) return NULL; if (pn->pn_type == tt) return pn; switch (pn->pn_arity) { case PN_LIST: for (pn2 = pn->pn_head; pn2; pn2 = pn2->pn_next) { pnt = ContainsStmt(pn2, tt); if (pnt) return pnt; } break; case PN_TERNARY: pnt = ContainsStmt(pn->pn_kid1, tt); if (pnt) return pnt; pnt = ContainsStmt(pn->pn_kid2, tt); if (pnt) return pnt; return ContainsStmt(pn->pn_kid3, tt); case PN_BINARY: /* * Limit recursion if pn is a binary expression, which can't contain a * var statement. */ if (pn->pn_op != JSOP_NOP) return NULL; pnt = ContainsStmt(pn->pn_left, tt); if (pnt) return pnt; return ContainsStmt(pn->pn_right, tt); case PN_UNARY: if (pn->pn_op != JSOP_NOP) return NULL; return ContainsStmt(pn->pn_kid, tt); case PN_NAME: return ContainsStmt(pn->pn_expr, tt); default:; } return NULL; } static JSParseNode * ReturnOrYield(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc, JSParser operandParser) { JSTokenType tt, tt2; JSParseNode *pn, *pn2; tt = CURRENT_TOKEN(ts).type; if (!(tc->flags & TCF_IN_FUNCTION)) { js_ReportCompileErrorNumber(cx, ts, JSREPORT_TS | JSREPORT_ERROR, JSMSG_BAD_RETURN_OR_YIELD, #if JS_HAS_GENERATORS (tt == TOK_YIELD) ? js_yield_str : #endif js_return_str); return NULL; } pn = NewParseNode(cx, ts, PN_UNARY, tc); if (!pn) return NULL; #if JS_HAS_GENERATORS if (tt == TOK_YIELD) tc->flags |= TCF_FUN_IS_GENERATOR; #endif /* This is ugly, but we don't want to require a semicolon. */ ts->flags |= TSF_OPERAND; tt2 = js_PeekTokenSameLine(cx, ts); ts->flags &= ~TSF_OPERAND; if (tt2 == TOK_ERROR) return NULL; if (tt2 != TOK_EOF && tt2 != TOK_EOL && tt2 != TOK_SEMI && tt2 != TOK_RC #if JS_HAS_GENERATORS && (tt != TOK_YIELD || (tt2 != tt && tt2 != TOK_RB && tt2 != TOK_RP)) #endif ) { pn2 = operandParser(cx, ts, tc); if (!pn2) return NULL; #if JS_HAS_GENERATORS if (tt == TOK_RETURN) #endif tc->flags |= TCF_RETURN_EXPR; pn->pn_pos.end = pn2->pn_pos.end; pn->pn_kid = pn2; } else { #if JS_HAS_GENERATORS if (tt == TOK_RETURN) #endif tc->flags |= TCF_RETURN_VOID; pn->pn_kid = NULL; } if ((~tc->flags & (TCF_RETURN_EXPR | TCF_FUN_IS_GENERATOR)) == 0) { /* As in Python (see PEP-255), disallow return v; in generators. */ ReportBadReturn(cx, ts, JSREPORT_ERROR, JSMSG_BAD_GENERATOR_RETURN, JSMSG_BAD_ANON_GENERATOR_RETURN); return NULL; } if (JS_HAS_STRICT_OPTION(cx) && (~tc->flags & (TCF_RETURN_EXPR | TCF_RETURN_VOID)) == 0 && !ReportBadReturn(cx, ts, JSREPORT_WARNING | JSREPORT_STRICT, JSMSG_NO_RETURN_VALUE, JSMSG_ANON_NO_RETURN_VALUE)) { return NULL; } return pn; } static JSParseNode * PushLexicalScope(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc, JSStmtInfo *stmtInfo) { JSParseNode *pn; JSObject *obj; JSAtom *atom; pn = NewParseNode(cx, ts, PN_NAME, tc); if (!pn) return NULL; obj = js_NewBlockObject(cx); if (!obj) return NULL; atom = js_AtomizeObject(cx, obj, 0); if (!atom) return NULL; js_PushBlockScope(tc, stmtInfo, atom, -1); pn->pn_type = TOK_LEXICALSCOPE; pn->pn_op = JSOP_LEAVEBLOCK; pn->pn_atom = atom; pn->pn_expr = NULL; pn->pn_slot = -1; pn->pn_attrs = 0; return pn; } #if JS_HAS_BLOCK_SCOPE static JSParseNode * LetBlock(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc, JSBool statement) { JSParseNode *pn, *pnblock, *pnlet; JSStmtInfo stmtInfo; JS_ASSERT(CURRENT_TOKEN(ts).type == TOK_LET); /* Create the let binary node. */ pnlet = NewParseNode(cx, ts, PN_BINARY, tc); if (!pnlet) return NULL; MUST_MATCH_TOKEN(TOK_LP, JSMSG_PAREN_BEFORE_LET); /* This is a let block or expression of the form: let (a, b, c) .... */ pnblock = PushLexicalScope(cx, ts, tc, &stmtInfo); if (!pnblock) return NULL; pn = pnblock; pn->pn_expr = pnlet; pnlet->pn_left = Variables(cx, ts, tc); if (!pnlet->pn_left) return NULL; pnlet->pn_left->pn_extra = PNX_POPVAR; MUST_MATCH_TOKEN(TOK_RP, JSMSG_PAREN_AFTER_LET); ts->flags |= TSF_OPERAND; if (statement && !js_MatchToken(cx, ts, TOK_LC)) { /* * If this is really an expression in let statement guise, then we * need to wrap the TOK_LET node in a TOK_SEMI node so that we pop * the return value of the expression. */ pn = NewParseNode(cx, ts, PN_UNARY, tc); if (!pn) return NULL; pn->pn_type = TOK_SEMI; pn->pn_num = -1; pn->pn_kid = pnblock; statement = JS_FALSE; } ts->flags &= ~TSF_OPERAND; if (statement) { pnlet->pn_right = Statements(cx, ts, tc); if (!pnlet->pn_right) return NULL; MUST_MATCH_TOKEN(TOK_RC, JSMSG_CURLY_AFTER_LET); } else { /* * Change pnblock's opcode to the variant that propagates the last * result down after popping the block, and clear statement. */ pnblock->pn_op = JSOP_LEAVEBLOCKEXPR; pnlet->pn_right = Expr(cx, ts, tc); if (!pnlet->pn_right) return NULL; } js_PopStatement(tc); return pn; } #endif /* JS_HAS_BLOCK_SCOPE */ static JSParseNode * Statement(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc) { JSTokenType tt; JSParseNode *pn, *pn1, *pn2, *pn3, *pn4; JSStmtInfo stmtInfo, *stmt, *stmt2; JSAtom *label; CHECK_RECURSION(); ts->flags |= TSF_OPERAND; tt = js_GetToken(cx, ts); ts->flags &= ~TSF_OPERAND; #if JS_HAS_GETTER_SETTER if (tt == TOK_NAME) { tt = CheckGetterOrSetter(cx, ts, TOK_FUNCTION); if (tt == TOK_ERROR) return NULL; } #endif switch (tt) { #if JS_HAS_EXPORT_IMPORT case TOK_EXPORT: pn = NewParseNode(cx, ts, PN_LIST, tc); if (!pn) return NULL; PN_INIT_LIST(pn); if (js_MatchToken(cx, ts, TOK_STAR)) { pn2 = NewParseNode(cx, ts, PN_NULLARY, tc); if (!pn2) return NULL; PN_APPEND(pn, pn2); } else { do { MUST_MATCH_TOKEN(TOK_NAME, JSMSG_NO_EXPORT_NAME); pn2 = NewParseNode(cx, ts, PN_NAME, tc); if (!pn2) return NULL; pn2->pn_op = JSOP_NAME; pn2->pn_atom = CURRENT_TOKEN(ts).t_atom; pn2->pn_expr = NULL; pn2->pn_slot = -1; pn2->pn_attrs = 0; PN_APPEND(pn, pn2); } while (js_MatchToken(cx, ts, TOK_COMMA)); } pn->pn_pos.end = PN_LAST(pn)->pn_pos.end; tc->flags |= TCF_FUN_HEAVYWEIGHT; break; case TOK_IMPORT: pn = NewParseNode(cx, ts, PN_LIST, tc); if (!pn) return NULL; PN_INIT_LIST(pn); do { pn2 = ImportExpr(cx, ts, tc); if (!pn2) return NULL; PN_APPEND(pn, pn2); } while (js_MatchToken(cx, ts, TOK_COMMA)); pn->pn_pos.end = PN_LAST(pn)->pn_pos.end; tc->flags |= TCF_FUN_HEAVYWEIGHT; break; #endif /* JS_HAS_EXPORT_IMPORT */ case TOK_FUNCTION: #if JS_HAS_XML_SUPPORT ts->flags |= TSF_KEYWORD_IS_NAME; tt = js_PeekToken(cx, ts); ts->flags &= ~TSF_KEYWORD_IS_NAME; if (tt == TOK_DBLCOLON) goto expression; #endif return FunctionStmt(cx, ts, tc); case TOK_IF: /* An IF node has three kids: condition, then, and optional else. */ pn = NewParseNode(cx, ts, PN_TERNARY, tc); if (!pn) return NULL; pn1 = Condition(cx, ts, tc); if (!pn1) return NULL; js_PushStatement(tc, &stmtInfo, STMT_IF, -1); pn2 = Statement(cx, ts, tc); if (!pn2) return NULL; ts->flags |= TSF_OPERAND; if (js_MatchToken(cx, ts, TOK_ELSE)) { ts->flags &= ~TSF_OPERAND; stmtInfo.type = STMT_ELSE; pn3 = Statement(cx, ts, tc); if (!pn3) return NULL; pn->pn_pos.end = pn3->pn_pos.end; } else { ts->flags &= ~TSF_OPERAND; pn3 = NULL; pn->pn_pos.end = pn2->pn_pos.end; } js_PopStatement(tc); pn->pn_kid1 = pn1; pn->pn_kid2 = pn2; pn->pn_kid3 = pn3; return pn; case TOK_SWITCH: { JSParseNode *pn5, *saveBlock; JSBool seenDefault = JS_FALSE; pn = NewParseNode(cx, ts, PN_BINARY, tc); if (!pn) return NULL; MUST_MATCH_TOKEN(TOK_LP, JSMSG_PAREN_BEFORE_SWITCH); /* pn1 points to the switch's discriminant. */ pn1 = Expr(cx, ts, tc); if (!pn1) return NULL; MUST_MATCH_TOKEN(TOK_RP, JSMSG_PAREN_AFTER_SWITCH); MUST_MATCH_TOKEN(TOK_LC, JSMSG_CURLY_BEFORE_SWITCH); /* pn2 is a list of case nodes. The default case has pn_left == NULL */ pn2 = NewParseNode(cx, ts, PN_LIST, tc); if (!pn2) return NULL; saveBlock = tc->blockNode; tc->blockNode = pn2; PN_INIT_LIST(pn2); js_PushStatement(tc, &stmtInfo, STMT_SWITCH, -1); while ((tt = js_GetToken(cx, ts)) != TOK_RC) { switch (tt) { case TOK_DEFAULT: if (seenDefault) { js_ReportCompileErrorNumber(cx, ts, JSREPORT_TS | JSREPORT_ERROR, JSMSG_TOO_MANY_DEFAULTS); return NULL; } seenDefault = JS_TRUE; /* fall through */ case TOK_CASE: pn3 = NewParseNode(cx, ts, PN_BINARY, tc); if (!pn3) return NULL; if (tt == TOK_DEFAULT) { pn3->pn_left = NULL; } else { pn3->pn_left = Expr(cx, ts, tc); if (!pn3->pn_left) return NULL; } PN_APPEND(pn2, pn3); if (pn2->pn_count == JS_BIT(16)) { js_ReportCompileErrorNumber(cx, ts, JSREPORT_TS | JSREPORT_ERROR, JSMSG_TOO_MANY_CASES); return NULL; } break; case TOK_ERROR: return NULL; default: js_ReportCompileErrorNumber(cx, ts, JSREPORT_TS | JSREPORT_ERROR, JSMSG_BAD_SWITCH); return NULL; } MUST_MATCH_TOKEN(TOK_COLON, JSMSG_COLON_AFTER_CASE); pn4 = NewParseNode(cx, ts, PN_LIST, tc); if (!pn4) return NULL; pn4->pn_type = TOK_LC; PN_INIT_LIST(pn4); ts->flags |= TSF_OPERAND; while ((tt = js_PeekToken(cx, ts)) != TOK_RC && tt != TOK_CASE && tt != TOK_DEFAULT) { ts->flags &= ~TSF_OPERAND; if (tt == TOK_ERROR) return NULL; pn5 = Statement(cx, ts, tc); if (!pn5) return NULL; pn4->pn_pos.end = pn5->pn_pos.end; PN_APPEND(pn4, pn5); ts->flags |= TSF_OPERAND; } ts->flags &= ~TSF_OPERAND; /* Fix the PN_LIST so it doesn't begin at the TOK_COLON. */ if (pn4->pn_head) pn4->pn_pos.begin = pn4->pn_head->pn_pos.begin; pn3->pn_pos.end = pn4->pn_pos.end; pn3->pn_right = pn4; } /* * Handle the case where there was a let declaration in any case in * the switch body, but not within an inner block. If it replaced * tc->blockNode with a new block node then we must refresh pn2 and * then restore tc->blockNode. */ if (tc->blockNode != pn2) pn2 = tc->blockNode; tc->blockNode = saveBlock; js_PopStatement(tc); pn->pn_pos.end = pn2->pn_pos.end = CURRENT_TOKEN(ts).pos.end; pn->pn_left = pn1; pn->pn_right = pn2; return pn; } case TOK_WHILE: pn = NewParseNode(cx, ts, PN_BINARY, tc); if (!pn) return NULL; js_PushStatement(tc, &stmtInfo, STMT_WHILE_LOOP, -1); pn2 = Condition(cx, ts, tc); if (!pn2) return NULL; pn->pn_left = pn2; pn2 = Statement(cx, ts, tc); if (!pn2) return NULL; js_PopStatement(tc); pn->pn_pos.end = pn2->pn_pos.end; pn->pn_right = pn2; return pn; case TOK_DO: pn = NewParseNode(cx, ts, PN_BINARY, tc); if (!pn) return NULL; js_PushStatement(tc, &stmtInfo, STMT_DO_LOOP, -1); pn2 = Statement(cx, ts, tc); if (!pn2) return NULL; pn->pn_left = pn2; MUST_MATCH_TOKEN(TOK_WHILE, JSMSG_WHILE_AFTER_DO); pn2 = Condition(cx, ts, tc); if (!pn2) return NULL; js_PopStatement(tc); pn->pn_pos.end = pn2->pn_pos.end; pn->pn_right = pn2; if ((cx->version & JSVERSION_MASK) != JSVERSION_ECMA_3) { /* * All legacy and extended versions must do automatic semicolon * insertion after do-while. See the testcase and discussion in * http://bugzilla.mozilla.org/show_bug.cgi?id=238945. */ (void) js_MatchToken(cx, ts, TOK_SEMI); return pn; } break; case TOK_FOR: { #if JS_HAS_BLOCK_SCOPE JSParseNode *pnlet; JSStmtInfo blockInfo; pnlet = NULL; #endif /* A FOR node is binary, left is loop control and right is the body. */ pn = NewParseNode(cx, ts, PN_BINARY, tc); if (!pn) return NULL; js_PushStatement(tc, &stmtInfo, STMT_FOR_LOOP, -1); pn->pn_op = JSOP_FORIN; if (js_MatchToken(cx, ts, TOK_NAME)) { if (CURRENT_TOKEN(ts).t_atom == cx->runtime->atomState.eachAtom) pn->pn_op = JSOP_FOREACH; else js_UngetToken(ts); } MUST_MATCH_TOKEN(TOK_LP, JSMSG_PAREN_AFTER_FOR); ts->flags |= TSF_OPERAND; tt = js_PeekToken(cx, ts); ts->flags &= ~TSF_OPERAND; if (tt == TOK_SEMI) { if (pn->pn_op == JSOP_FOREACH) goto bad_for_each; /* No initializer -- set first kid of left sub-node to null. */ pn1 = NULL; } else { /* * Set pn1 to a var list or an initializing expression. * * Set the TCF_IN_FOR_INIT flag during parsing of the first clause * of the for statement. This flag will be used by the RelExpr * production; if it is set, then the 'in' keyword will not be * recognized as an operator, leaving it available to be parsed as * part of a for/in loop. * * A side effect of this restriction is that (unparenthesized) * expressions involving an 'in' operator are illegal in the init * clause of an ordinary for loop. */ tc->flags |= TCF_IN_FOR_INIT; if (tt == TOK_VAR) { (void) js_GetToken(cx, ts); pn1 = Variables(cx, ts, tc); #if JS_HAS_BLOCK_SCOPE } else if (tt == TOK_LET) { (void) js_GetToken(cx, ts); if (js_PeekToken(cx, ts) == TOK_LP) { pn1 = LetBlock(cx, ts, tc, JS_FALSE); tt = TOK_LEXICALSCOPE; } else { pnlet = PushLexicalScope(cx, ts, tc, &blockInfo); if (!pnlet) return NULL; pn1 = Variables(cx, ts, tc); } #endif } else { pn1 = Expr(cx, ts, tc); if (pn1) { while (pn1->pn_type == TOK_RP) pn1 = pn1->pn_kid; } } tc->flags &= ~TCF_IN_FOR_INIT; if (!pn1) return NULL; } /* * We can be sure that it's a for/in loop if there's still an 'in' * keyword here, even if JavaScript recognizes 'in' as an operator, * as we've excluded 'in' from being parsed in RelExpr by setting * the TCF_IN_FOR_INIT flag in our JSTreeContext. */ if (pn1 && js_MatchToken(cx, ts, TOK_IN)) { stmtInfo.type = STMT_FOR_IN_LOOP; /* Check that the left side of the 'in' is valid. */ JS_ASSERT(!TOKEN_TYPE_IS_DECL(tt) || pn1->pn_type == tt); if (TOKEN_TYPE_IS_DECL(tt) ? (pn1->pn_count > 1 || pn1->pn_op == JSOP_DEFCONST #if JS_HAS_DESTRUCTURING || (pn->pn_op == JSOP_FORIN && (pn1->pn_head->pn_type == TOK_RC || (pn1->pn_head->pn_type == TOK_RB && pn1->pn_head->pn_count != 2) || (pn1->pn_head->pn_type == TOK_ASSIGN && (pn1->pn_head->pn_left->pn_type != TOK_RB || pn1->pn_head->pn_left->pn_count != 2)))) #endif ) : (pn1->pn_type != TOK_NAME && pn1->pn_type != TOK_DOT && #if JS_HAS_DESTRUCTURING ((pn->pn_op == JSOP_FORIN) ? (pn1->pn_type != TOK_RB || pn1->pn_count != 2) : (pn1->pn_type != TOK_RB && pn1->pn_type != TOK_RC)) && #endif #if JS_HAS_LVALUE_RETURN pn1->pn_type != TOK_LP && #endif #if JS_HAS_XML_SUPPORT (pn1->pn_type != TOK_UNARYOP || pn1->pn_op != JSOP_XMLNAME) && #endif pn1->pn_type != TOK_LB)) { js_ReportCompileErrorNumber(cx, ts, JSREPORT_TS | JSREPORT_ERROR, JSMSG_BAD_FOR_LEFTSIDE); return NULL; } if (TOKEN_TYPE_IS_DECL(tt)) { /* Tell js_EmitTree(TOK_VAR) that pn1 is part of a for/in. */ pn1->pn_extra |= PNX_FORINVAR; /* * Generate a final POP only if the variable is a simple name * (which means it is not a destructuring left-hand side) and * it has an initializer. */ pn2 = pn1->pn_head; if (pn2->pn_type == TOK_NAME && pn2->pn_expr) pn1->pn_extra |= PNX_POPVAR; } else { pn2 = pn1; #if JS_HAS_LVALUE_RETURN if (pn2->pn_type == TOK_LP) pn2->pn_op = JSOP_SETCALL; #endif #if JS_HAS_XML_SUPPORT if (pn2->pn_type == TOK_UNARYOP) pn2->pn_op = JSOP_BINDXMLNAME; #endif } switch (pn2->pn_type) { case TOK_NAME: /* Beware 'for (arguments in ...)' with or without a 'var'. */ if (pn2->pn_atom == cx->runtime->atomState.argumentsAtom) tc->flags |= TCF_FUN_HEAVYWEIGHT; break; #if JS_HAS_DESTRUCTURING case TOK_ASSIGN: pn2 = pn2->pn_left; JS_ASSERT(pn2->pn_type == TOK_RB || pn2->pn_type == TOK_RC); /* FALL THROUGH */ case TOK_RB: case TOK_RC: /* Check for valid lvalues in var-less destructuring for-in. */ if (pn1 == pn2 && !CheckDestructuring(cx, NULL, pn2, NULL, tc)) return NULL; /* Destructuring for-in requires [key, value] enumeration. */ if (pn->pn_op != JSOP_FOREACH) pn->pn_op = JSOP_FOREACHKEYVAL; break; #endif default:; } /* Parse the object expression as the right operand of 'in'. */ pn2 = NewBinary(cx, TOK_IN, JSOP_NOP, pn1, Expr(cx, ts, tc), tc); if (!pn2) return NULL; pn->pn_left = pn2; } else { if (pn->pn_op == JSOP_FOREACH) goto bad_for_each; pn->pn_op = JSOP_NOP; /* Parse the loop condition or null into pn2. */ MUST_MATCH_TOKEN(TOK_SEMI, JSMSG_SEMI_AFTER_FOR_INIT); ts->flags |= TSF_OPERAND; tt = js_PeekToken(cx, ts); ts->flags &= ~TSF_OPERAND; if (tt == TOK_SEMI) { pn2 = NULL; } else { pn2 = Expr(cx, ts, tc); if (!pn2) return NULL; } /* Parse the update expression or null into pn3. */ MUST_MATCH_TOKEN(TOK_SEMI, JSMSG_SEMI_AFTER_FOR_COND); ts->flags |= TSF_OPERAND; tt = js_PeekToken(cx, ts); ts->flags &= ~TSF_OPERAND; if (tt == TOK_RP) { pn3 = NULL; } else { pn3 = Expr(cx, ts, tc); if (!pn3) return NULL; } /* Build the RESERVED node to use as the left kid of pn. */ pn4 = NewParseNode(cx, ts, PN_TERNARY, tc); if (!pn4) return NULL; pn4->pn_type = TOK_RESERVED; pn4->pn_op = JSOP_NOP; pn4->pn_kid1 = pn1; pn4->pn_kid2 = pn2; pn4->pn_kid3 = pn3; pn->pn_left = pn4; } MUST_MATCH_TOKEN(TOK_RP, JSMSG_PAREN_AFTER_FOR_CTRL); /* Parse the loop body into pn->pn_right. */ pn2 = Statement(cx, ts, tc); if (!pn2) return NULL; pn->pn_right = pn2; /* Record the absolute line number for source note emission. */ pn->pn_pos.end = pn2->pn_pos.end; #if JS_HAS_BLOCK_SCOPE if (pnlet) { js_PopStatement(tc); pnlet->pn_expr = pn; pn = pnlet; } #endif js_PopStatement(tc); return pn; bad_for_each: js_ReportCompileErrorNumber(cx, pn, JSREPORT_PN | JSREPORT_ERROR, JSMSG_BAD_FOR_EACH_LOOP); return NULL; } case TOK_TRY: { JSParseNode *catchList, *lastCatch; /* * try nodes are ternary. * kid1 is the try Statement * kid2 is the catch node list or null * kid3 is the finally Statement * * catch nodes are ternary. * kid1 is the lvalue (TOK_NAME, TOK_LB, or TOK_LC) * kid2 is the catch guard or null if no guard * kid3 is the catch block * * catch lvalue nodes are either: * TOK_NAME for a single identifier * TOK_RB or TOK_RC for a destructuring left-hand side * * finally nodes are TOK_LC Statement lists. */ pn = NewParseNode(cx, ts, PN_TERNARY, tc); if (!pn) return NULL; pn->pn_op = JSOP_NOP; MUST_MATCH_TOKEN(TOK_LC, JSMSG_CURLY_BEFORE_TRY); js_PushStatement(tc, &stmtInfo, STMT_TRY, -1); pn->pn_kid1 = Statements(cx, ts, tc); if (!pn->pn_kid1) return NULL; MUST_MATCH_TOKEN(TOK_RC, JSMSG_CURLY_AFTER_TRY); js_PopStatement(tc); catchList = NULL; tt = js_GetToken(cx, ts); if (tt == TOK_CATCH) { catchList = NewParseNode(cx, ts, PN_LIST, tc); if (!catchList) return NULL; catchList->pn_type = TOK_RESERVED; PN_INIT_LIST(catchList); lastCatch = NULL; do { JSParseNode *pnblock; BindData data; /* Check for another catch after unconditional catch. */ if (lastCatch && !lastCatch->pn_kid2) { js_ReportCompileErrorNumber(cx, ts, JSREPORT_TS | JSREPORT_ERROR, JSMSG_CATCH_AFTER_GENERAL); return NULL; } /* * Create a lexical scope node around the whole catch clause, * including the head. */ pnblock = PushLexicalScope(cx, ts, tc, &stmtInfo); if (!pnblock) return NULL; stmtInfo.type = STMT_CATCH; /* * Legal catch forms are: * catch (lhs) * catch (lhs if ) * where lhs is a name or a destructuring left-hand side. * (the latter is legal only #ifdef JS_HAS_CATCH_GUARD) */ pn2 = NewParseNode(cx, ts, PN_TERNARY, tc); if (!pn2) return NULL; pnblock->pn_expr = pn2; MUST_MATCH_TOKEN(TOK_LP, JSMSG_PAREN_BEFORE_CATCH); /* * Contrary to ECMA Ed. 3, the catch variable is lexically * scoped, not a property of a new Object instance. This is * an intentional change that anticipates ECMA Ed. 4. */ data.pn = NULL; data.ts = ts; data.obj = tc->blockChain; data.op = JSOP_NOP; data.binder = BindLet; data.u.let.index = 0; data.u.let.overflow = JSMSG_TOO_MANY_CATCH_VARS; tt = js_GetToken(cx, ts); switch (tt) { #if JS_HAS_DESTRUCTURING case TOK_LB: case TOK_LC: pn3 = DestructuringExpr(cx, &data, tc, tt); if (!pn3) return NULL; break; #endif case TOK_NAME: label = CURRENT_TOKEN(ts).t_atom; if (!data.binder(cx, &data, label, tc)) return NULL; pn3 = NewParseNode(cx, ts, PN_NAME, tc); if (!pn3) return NULL; pn3->pn_atom = label; pn3->pn_expr = NULL; pn3->pn_slot = 0; pn3->pn_attrs = 0; break; default: js_ReportCompileErrorNumber(cx, ts, JSREPORT_TS | JSREPORT_ERROR, JSMSG_CATCH_IDENTIFIER); return NULL; } pn2->pn_kid1 = pn3; pn2->pn_kid2 = NULL; #if JS_HAS_CATCH_GUARD /* * We use 'catch (x if x === 5)' (not 'catch (x : x === 5)') * to avoid conflicting with the JS2/ECMAv4 type annotation * catchguard syntax. */ if (js_MatchToken(cx, ts, TOK_IF)) { pn2->pn_kid2 = Expr(cx, ts, tc); if (!pn2->pn_kid2) return NULL; } #endif MUST_MATCH_TOKEN(TOK_RP, JSMSG_PAREN_AFTER_CATCH); MUST_MATCH_TOKEN(TOK_LC, JSMSG_CURLY_BEFORE_CATCH); pn2->pn_kid3 = Statements(cx, ts, tc); if (!pn2->pn_kid3) return NULL; MUST_MATCH_TOKEN(TOK_RC, JSMSG_CURLY_AFTER_CATCH); js_PopStatement(tc); PN_APPEND(catchList, pnblock); lastCatch = pn2; ts->flags |= TSF_OPERAND; tt = js_GetToken(cx, ts); ts->flags &= ~TSF_OPERAND; } while (tt == TOK_CATCH); } pn->pn_kid2 = catchList; if (tt == TOK_FINALLY) { tc->tryCount++; MUST_MATCH_TOKEN(TOK_LC, JSMSG_CURLY_BEFORE_FINALLY); js_PushStatement(tc, &stmtInfo, STMT_FINALLY, -1); pn->pn_kid3 = Statements(cx, ts, tc); if (!pn->pn_kid3) return NULL; MUST_MATCH_TOKEN(TOK_RC, JSMSG_CURLY_AFTER_FINALLY); js_PopStatement(tc); } else { js_UngetToken(ts); pn->pn_kid3 = NULL; } if (!catchList && !pn->pn_kid3) { js_ReportCompileErrorNumber(cx, ts, JSREPORT_TS | JSREPORT_ERROR, JSMSG_CATCH_OR_FINALLY); return NULL; } tc->tryCount++; return pn; } case TOK_THROW: pn = NewParseNode(cx, ts, PN_UNARY, tc); if (!pn) return NULL; /* ECMA-262 Edition 3 says 'throw [no LineTerminator here] Expr'. */ ts->flags |= TSF_OPERAND; tt = js_PeekTokenSameLine(cx, ts); ts->flags &= ~TSF_OPERAND; if (tt == TOK_ERROR) return NULL; if (tt == TOK_EOF || tt == TOK_EOL || tt == TOK_SEMI || tt == TOK_RC) { js_ReportCompileErrorNumber(cx, ts, JSREPORT_TS | JSREPORT_ERROR, JSMSG_SYNTAX_ERROR); return NULL; } pn2 = Expr(cx, ts, tc); if (!pn2) return NULL; pn->pn_pos.end = pn2->pn_pos.end; pn->pn_op = JSOP_THROW; pn->pn_kid = pn2; break; /* TOK_CATCH and TOK_FINALLY are both handled in the TOK_TRY case */ case TOK_CATCH: js_ReportCompileErrorNumber(cx, ts, JSREPORT_TS | JSREPORT_ERROR, JSMSG_CATCH_WITHOUT_TRY); return NULL; case TOK_FINALLY: js_ReportCompileErrorNumber(cx, ts, JSREPORT_TS | JSREPORT_ERROR, JSMSG_FINALLY_WITHOUT_TRY); return NULL; case TOK_BREAK: pn = NewParseNode(cx, ts, PN_NULLARY, tc); if (!pn) return NULL; if (!MatchLabel(cx, ts, pn)) return NULL; stmt = tc->topStmt; label = pn->pn_atom; if (label) { for (; ; stmt = stmt->down) { if (!stmt) { js_ReportCompileErrorNumber(cx, ts, JSREPORT_TS | JSREPORT_ERROR, JSMSG_LABEL_NOT_FOUND); return NULL; } if (stmt->type == STMT_LABEL && stmt->atom == label) break; } } else { for (; ; stmt = stmt->down) { if (!stmt) { js_ReportCompileErrorNumber(cx, ts, JSREPORT_TS | JSREPORT_ERROR, JSMSG_TOUGH_BREAK); return NULL; } if (STMT_IS_LOOP(stmt) || stmt->type == STMT_SWITCH) break; } } if (label) pn->pn_pos.end = CURRENT_TOKEN(ts).pos.end; break; case TOK_CONTINUE: pn = NewParseNode(cx, ts, PN_NULLARY, tc); if (!pn) return NULL; if (!MatchLabel(cx, ts, pn)) return NULL; stmt = tc->topStmt; label = pn->pn_atom; if (label) { for (stmt2 = NULL; ; stmt = stmt->down) { if (!stmt) { js_ReportCompileErrorNumber(cx, ts, JSREPORT_TS | JSREPORT_ERROR, JSMSG_LABEL_NOT_FOUND); return NULL; } if (stmt->type == STMT_LABEL) { if (stmt->atom == label) { if (!stmt2 || !STMT_IS_LOOP(stmt2)) { js_ReportCompileErrorNumber(cx, ts, JSREPORT_TS | JSREPORT_ERROR, JSMSG_BAD_CONTINUE); return NULL; } break; } } else { stmt2 = stmt; } } } else { for (; ; stmt = stmt->down) { if (!stmt) { js_ReportCompileErrorNumber(cx, ts, JSREPORT_TS | JSREPORT_ERROR, JSMSG_BAD_CONTINUE); return NULL; } if (STMT_IS_LOOP(stmt)) break; } } if (label) pn->pn_pos.end = CURRENT_TOKEN(ts).pos.end; break; case TOK_WITH: pn = NewParseNode(cx, ts, PN_BINARY, tc); if (!pn) return NULL; MUST_MATCH_TOKEN(TOK_LP, JSMSG_PAREN_BEFORE_WITH); pn2 = Expr(cx, ts, tc); if (!pn2) return NULL; MUST_MATCH_TOKEN(TOK_RP, JSMSG_PAREN_AFTER_WITH); pn->pn_left = pn2; js_PushStatement(tc, &stmtInfo, STMT_WITH, -1); pn2 = Statement(cx, ts, tc); if (!pn2) return NULL; js_PopStatement(tc); pn->pn_pos.end = pn2->pn_pos.end; pn->pn_right = pn2; tc->flags |= TCF_FUN_HEAVYWEIGHT; return pn; case TOK_VAR: pn = Variables(cx, ts, tc); if (!pn) return NULL; /* Tell js_EmitTree to generate a final POP. */ pn->pn_extra |= PNX_POPVAR; break; #if JS_HAS_BLOCK_SCOPE case TOK_LET: { JSStmtInfo **sip; JSObject *obj; JSAtom *atom; /* Check for a let statement or let expression. */ if (js_PeekToken(cx, ts) == TOK_LP) { pn = LetBlock(cx, ts, tc, JS_TRUE); if (!pn || pn->pn_op == JSOP_LEAVEBLOCK) return pn; /* Let expressions require automatic semicolon insertion. */ JS_ASSERT(pn->pn_type == TOK_SEMI || pn->pn_op == JSOP_LEAVEBLOCKEXPR); break; } /* * This is a let declaration. We must convert the nearest JSStmtInfo * that is a block or a switch body to be our scope statement. Further * let declarations in this block will find this scope statement and * use the same block object. If we are the first let declaration in * this block (i.e., when the nearest maybe-scope JSStmtInfo isn't a * scope statement) then we also need to set tc->blockNode to be our * TOK_LEXICALSCOPE. */ sip = &tc->topScopeStmt; for (stmt = tc->topStmt; stmt; stmt = stmt->down) { if (STMT_MAYBE_SCOPE(stmt)) break; if (stmt == *sip) sip = &stmt->downScope; } if (stmt && (stmt->flags & SIF_SCOPE)) { JS_ASSERT(tc->blockChain == ATOM_TO_OBJECT(stmt->atom)); obj = tc->blockChain; } else { if (!stmt) { /* * FIXME: https://bugzilla.mozilla.org/show_bug.cgi?id=346749 * * This is a hard case that requires more work. In particular, * in many cases, we're trying to emit code as we go. However, * this means that we haven't necessarily finished processing * all let declarations in the implicit top-level block when * we emit a reference to one of them. For now, punt on this * and pretend this is a var declaration. */ CURRENT_TOKEN(ts).type = TOK_VAR; CURRENT_TOKEN(ts).t_op = JSOP_DEFVAR; pn = Variables(cx, ts, tc); if (!pn) return NULL; pn->pn_extra |= PNX_POPVAR; break; } /* Convert the block statement into a scope statement. */ obj = js_NewBlockObject(cx); if (!obj) return NULL; atom = js_AtomizeObject(cx, obj, 0); if (!atom) return NULL; /* * Insert stmt on the tc->topScopeStmt/stmtInfo.downScope linked * list stack, if it isn't already there. If it is there, but it * lacks the SIF_SCOPE flag, it must be a try, catch, or finally * block. */ JS_ASSERT(!(stmt->flags & SIF_SCOPE)); stmt->flags |= SIF_SCOPE; if (stmt != *sip) { JS_ASSERT(!stmt->downScope); JS_ASSERT(stmt->type == STMT_BLOCK || stmt->type == STMT_SWITCH || stmt->type == STMT_TRY || stmt->type == STMT_FINALLY); stmt->downScope = *sip; *sip = stmt; } else { JS_ASSERT(stmt->type == STMT_CATCH); JS_ASSERT(stmt->downScope); } obj->slots[JSSLOT_PARENT] = OBJECT_TO_JSVAL(tc->blockChain); tc->blockChain = obj; stmt->atom = atom; #ifdef DEBUG pn1 = tc->blockNode; JS_ASSERT(!pn1 || pn1->pn_type != TOK_LEXICALSCOPE); #endif /* Create a new lexical scope node for these statements. */ pn1 = NewParseNode(cx, ts, PN_NAME, tc); if (!pn1) return NULL; pn1->pn_type = TOK_LEXICALSCOPE; pn1->pn_op = JSOP_LEAVEBLOCK; pn1->pn_pos = tc->blockNode->pn_pos; pn1->pn_atom = atom; pn1->pn_expr = tc->blockNode; pn1->pn_slot = -1; pn1->pn_attrs = 0; tc->blockNode = pn1; } pn = Variables(cx, ts, tc); if (!pn) return NULL; pn->pn_extra = PNX_POPVAR; break; } #endif /* JS_HAS_BLOCK_SCOPE */ case TOK_RETURN: pn = ReturnOrYield(cx, ts, tc, Expr); if (!pn) return NULL; break; case TOK_LC: { uintN oldflags; oldflags = tc->flags; tc->flags = oldflags & ~TCF_HAS_FUNCTION_STMT; js_PushStatement(tc, &stmtInfo, STMT_BLOCK, -1); pn = Statements(cx, ts, tc); if (!pn) return NULL; MUST_MATCH_TOKEN(TOK_RC, JSMSG_CURLY_IN_COMPOUND); js_PopStatement(tc); /* * If we contain a function statement and our container is top-level * or another block, flag pn to preserve braces when decompiling. */ if ((tc->flags & TCF_HAS_FUNCTION_STMT) && (!tc->topStmt || tc->topStmt->type == STMT_BLOCK)) { pn->pn_extra |= PNX_NEEDBRACES; } tc->flags = oldflags | (tc->flags & (TCF_FUN_FLAGS | TCF_RETURN_FLAGS)); return pn; } case TOK_EOL: case TOK_SEMI: pn = NewParseNode(cx, ts, PN_UNARY, tc); if (!pn) return NULL; pn->pn_type = TOK_SEMI; pn->pn_kid = NULL; return pn; #if JS_HAS_DEBUGGER_KEYWORD case TOK_DEBUGGER: pn = NewParseNode(cx, ts, PN_NULLARY, tc); if (!pn) return NULL; pn->pn_type = TOK_DEBUGGER; tc->flags |= TCF_FUN_HEAVYWEIGHT; break; #endif /* JS_HAS_DEBUGGER_KEYWORD */ #if JS_HAS_XML_SUPPORT case TOK_DEFAULT: pn = NewParseNode(cx, ts, PN_UNARY, tc); if (!pn) return NULL; if (!js_MatchToken(cx, ts, TOK_NAME) || CURRENT_TOKEN(ts).t_atom != cx->runtime->atomState.xmlAtom || !js_MatchToken(cx, ts, TOK_NAME) || CURRENT_TOKEN(ts).t_atom != cx->runtime->atomState.namespaceAtom || !js_MatchToken(cx, ts, TOK_ASSIGN) || CURRENT_TOKEN(ts).t_op != JSOP_NOP) { js_ReportCompileErrorNumber(cx, ts, JSREPORT_TS | JSREPORT_ERROR, JSMSG_BAD_DEFAULT_XML_NAMESPACE); return NULL; } pn2 = Expr(cx, ts, tc); if (!pn2) return NULL; pn->pn_op = JSOP_DEFXMLNS; pn->pn_pos.end = pn2->pn_pos.end; pn->pn_kid = pn2; tc->flags |= TCF_HAS_DEFXMLNS; break; #endif case TOK_ERROR: return NULL; default: #if JS_HAS_XML_SUPPORT expression: #endif js_UngetToken(ts); pn2 = Expr(cx, ts, tc); if (!pn2) return NULL; if (js_PeekToken(cx, ts) == TOK_COLON) { if (pn2->pn_type != TOK_NAME) { js_ReportCompileErrorNumber(cx, ts, JSREPORT_TS | JSREPORT_ERROR, JSMSG_BAD_LABEL); return NULL; } label = pn2->pn_atom; for (stmt = tc->topStmt; stmt; stmt = stmt->down) { if (stmt->type == STMT_LABEL && stmt->atom == label) { js_ReportCompileErrorNumber(cx, ts, JSREPORT_TS | JSREPORT_ERROR, JSMSG_DUPLICATE_LABEL); return NULL; } } (void) js_GetToken(cx, ts); /* Push a label struct and parse the statement. */ js_PushStatement(tc, &stmtInfo, STMT_LABEL, -1); stmtInfo.atom = label; pn = Statement(cx, ts, tc); if (!pn) return NULL; /* Normalize empty statement to empty block for the decompiler. */ if (pn->pn_type == TOK_SEMI && !pn->pn_kid) { pn->pn_type = TOK_LC; pn->pn_arity = PN_LIST; PN_INIT_LIST(pn); } /* Pop the label, set pn_expr, and return early. */ js_PopStatement(tc); pn2->pn_type = TOK_COLON; pn2->pn_pos.end = pn->pn_pos.end; pn2->pn_expr = pn; return pn2; } pn = NewParseNode(cx, ts, PN_UNARY, tc); if (!pn) return NULL; pn->pn_type = TOK_SEMI; pn->pn_pos = pn2->pn_pos; pn->pn_kid = pn2; break; } /* Check termination of this primitive statement. */ if (ON_CURRENT_LINE(ts, pn->pn_pos)) { ts->flags |= TSF_OPERAND; tt = js_PeekTokenSameLine(cx, ts); ts->flags &= ~TSF_OPERAND; if (tt == TOK_ERROR) return NULL; if (tt != TOK_EOF && tt != TOK_EOL && tt != TOK_SEMI && tt != TOK_RC) { js_ReportCompileErrorNumber(cx, ts, JSREPORT_TS | JSREPORT_ERROR, JSMSG_SEMI_BEFORE_STMNT); return NULL; } } (void) js_MatchToken(cx, ts, TOK_SEMI); return pn; } static JSParseNode * Variables(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc) { JSTokenType tt; JSBool let; JSStmtInfo *scopeStmt; BindData data; JSParseNode *pn, *pn2; JSStackFrame *fp; JSAtom *atom; /* * The three options here are: * - TOK_LET: We are parsing a let declaration. * - TOK_LP: We are parsing the head of a let block. * - Otherwise, we're parsing var declarations. */ tt = CURRENT_TOKEN(ts).type; let = (tt == TOK_LET || tt == TOK_LP); JS_ASSERT(let || tt == TOK_VAR); /* Make sure that Statement set the tree context up correctly. */ scopeStmt = tc->topScopeStmt; if (let) { while (scopeStmt && !(scopeStmt->flags & SIF_SCOPE)) { JS_ASSERT(!STMT_MAYBE_SCOPE(scopeStmt)); scopeStmt = scopeStmt->downScope; } JS_ASSERT(scopeStmt); } data.pn = NULL; data.ts = ts; data.op = let ? JSOP_NOP : CURRENT_TOKEN(ts).t_op; data.binder = let ? BindLet : BindVarOrConst; pn = NewParseNode(cx, ts, PN_LIST, tc); if (!pn) return NULL; pn->pn_op = data.op; PN_INIT_LIST(pn); /* * The tricky part of this code is to create special parsenode opcodes for * getting and setting variables (which will be stored as special slots in * the frame). The most complicated case is an eval() inside a function. * If the evaluated string references variables in the enclosing function, * then we need to generate the special variable opcodes. We determine * this by looking up the variable's id in the current variable object. * Fortunately, we can avoid doing this for let declared variables. */ fp = cx->fp; if (let) { JS_ASSERT(tc->blockChain == ATOM_TO_OBJECT(scopeStmt->atom)); data.obj = tc->blockChain; data.u.let.index = OBJ_BLOCK_COUNT(cx, data.obj); data.u.let.overflow = JSMSG_TOO_MANY_FUN_VARS; } else { data.obj = fp->varobj; data.u.var.fun = fp->fun; data.u.var.clasp = OBJ_GET_CLASS(cx, data.obj); if (data.u.var.fun && data.u.var.clasp == &js_FunctionClass) { /* We are compiling code inside a function */ data.u.var.getter = js_GetLocalVariable; data.u.var.setter = js_SetLocalVariable; } else if (data.u.var.fun && data.u.var.clasp == &js_CallClass) { /* We are compiling code from an eval inside a function */ data.u.var.getter = js_GetCallVariable; data.u.var.setter = js_SetCallVariable; } else { data.u.var.getter = data.u.var.clasp->getProperty; data.u.var.setter = data.u.var.clasp->setProperty; } data.u.var.attrs = (data.op == JSOP_DEFCONST) ? JSPROP_PERMANENT | JSPROP_READONLY : JSPROP_PERMANENT; } do { tt = js_GetToken(cx, ts); #if JS_HAS_DESTRUCTURING if (tt == TOK_LB || tt == TOK_LC) { pn2 = PrimaryExpr(cx, ts, tc, tt, JS_FALSE); if (!pn2) return NULL; if ((tc->flags & TCF_IN_FOR_INIT) && js_PeekToken(cx, ts) == TOK_IN) { if (!CheckDestructuring(cx, &data, pn2, NULL, tc)) return NULL; PN_APPEND(pn, pn2); continue; } MUST_MATCH_TOKEN(TOK_ASSIGN, JSMSG_BAD_DESTRUCT_DECL); if (CURRENT_TOKEN(ts).t_op != JSOP_NOP) goto bad_var_init; pn2 = NewBinary(cx, TOK_ASSIGN, JSOP_NOP, pn2, AssignExpr(cx, ts, tc), tc); if (!pn2 || !CheckDestructuring(cx, &data, pn2->pn_left, pn2->pn_right, tc)) { return NULL; } PN_APPEND(pn, pn2); continue; } #endif if (tt != TOK_NAME) { js_ReportCompileErrorNumber(cx, ts, JSREPORT_TS | JSREPORT_ERROR, JSMSG_NO_VARIABLE_NAME); return NULL; } atom = CURRENT_TOKEN(ts).t_atom; if (!data.binder(cx, &data, atom, tc)) return NULL; pn2 = NewParseNode(cx, ts, PN_NAME, tc); if (!pn2) return NULL; pn2->pn_op = JSOP_NAME; pn2->pn_atom = atom; pn2->pn_expr = NULL; pn2->pn_slot = -1; pn2->pn_attrs = let ? 0 : data.u.var.attrs; PN_APPEND(pn, pn2); if (js_MatchToken(cx, ts, TOK_ASSIGN)) { if (CURRENT_TOKEN(ts).t_op != JSOP_NOP) goto bad_var_init; pn2->pn_expr = AssignExpr(cx, ts, tc); if (!pn2->pn_expr) return NULL; pn2->pn_op = (!let && data.op == JSOP_DEFCONST) ? JSOP_SETCONST : JSOP_SETNAME; if (!let && atom == cx->runtime->atomState.argumentsAtom) tc->flags |= TCF_FUN_HEAVYWEIGHT; } } while (js_MatchToken(cx, ts, TOK_COMMA)); pn->pn_pos.end = PN_LAST(pn)->pn_pos.end; return pn; bad_var_init: js_ReportCompileErrorNumber(cx, ts, JSREPORT_TS | JSREPORT_ERROR, JSMSG_BAD_VAR_INIT); return NULL; } static JSParseNode * Expr(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc) { JSParseNode *pn, *pn2; pn = AssignExpr(cx, ts, tc); if (pn && js_MatchToken(cx, ts, TOK_COMMA)) { pn2 = NewParseNode(cx, ts, PN_LIST, tc); if (!pn2) return NULL; pn2->pn_pos.begin = pn->pn_pos.begin; PN_INIT_LIST_1(pn2, pn); pn = pn2; do { #if JS_HAS_GENERATORS pn2 = PN_LAST(pn); if (pn2->pn_type == TOK_YIELD) { js_ReportCompileErrorNumber(cx, pn2, JSREPORT_PN | JSREPORT_ERROR, JSMSG_BAD_YIELD_SYNTAX); return NULL; } #endif pn2 = AssignExpr(cx, ts, tc); if (!pn2) return NULL; PN_APPEND(pn, pn2); } while (js_MatchToken(cx, ts, TOK_COMMA)); pn->pn_pos.end = PN_LAST(pn)->pn_pos.end; } return pn; } static JSParseNode * AssignExpr(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc) { JSParseNode *pn, *pn2; JSTokenType tt; JSOp op; CHECK_RECURSION(); #if JS_HAS_GENERATORS ts->flags |= TSF_OPERAND; if (js_MatchToken(cx, ts, TOK_YIELD)) { ts->flags &= ~TSF_OPERAND; return ReturnOrYield(cx, ts, tc, AssignExpr); } ts->flags &= ~TSF_OPERAND; #endif pn = CondExpr(cx, ts, tc); if (!pn) return NULL; tt = js_GetToken(cx, ts); #if JS_HAS_GETTER_SETTER if (tt == TOK_NAME) { tt = CheckGetterOrSetter(cx, ts, TOK_ASSIGN); if (tt == TOK_ERROR) return NULL; } #endif if (tt != TOK_ASSIGN) { js_UngetToken(ts); return pn; } op = CURRENT_TOKEN(ts).t_op; for (pn2 = pn; pn2->pn_type == TOK_RP; pn2 = pn2->pn_kid) continue; switch (pn2->pn_type) { case TOK_NAME: pn2->pn_op = JSOP_SETNAME; if (pn2->pn_atom == cx->runtime->atomState.argumentsAtom) tc->flags |= TCF_FUN_HEAVYWEIGHT; break; case TOK_DOT: pn2->pn_op = (pn2->pn_op == JSOP_GETMETHOD) ? JSOP_SETMETHOD : JSOP_SETPROP; break; case TOK_LB: pn2->pn_op = JSOP_SETELEM; break; #if JS_HAS_DESTRUCTURING case TOK_RB: case TOK_RC: if (op != JSOP_NOP) { js_ReportCompileErrorNumber(cx, ts, JSREPORT_TS | JSREPORT_ERROR, JSMSG_BAD_DESTRUCT_ASS); return NULL; } pn = AssignExpr(cx, ts, tc); if (!pn || !CheckDestructuring(cx, NULL, pn2, pn, tc)) return NULL; return NewBinary(cx, TOK_ASSIGN, op, pn2, pn, tc); #endif #if JS_HAS_LVALUE_RETURN case TOK_LP: JS_ASSERT(pn2->pn_op == JSOP_CALL || pn2->pn_op == JSOP_EVAL); pn2->pn_op = JSOP_SETCALL; break; #endif #if JS_HAS_XML_SUPPORT case TOK_UNARYOP: if (pn2->pn_op == JSOP_XMLNAME) { pn2->pn_op = JSOP_SETXMLNAME; break; } /* FALL THROUGH */ #endif default: js_ReportCompileErrorNumber(cx, ts, JSREPORT_TS | JSREPORT_ERROR, JSMSG_BAD_LEFTSIDE_OF_ASS); return NULL; } return NewBinary(cx, TOK_ASSIGN, op, pn2, AssignExpr(cx, ts, tc), tc); } static JSParseNode * CondExpr(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc) { JSParseNode *pn, *pn1, *pn2, *pn3; uintN oldflags; pn = OrExpr(cx, ts, tc); if (pn && js_MatchToken(cx, ts, TOK_HOOK)) { pn1 = pn; pn = NewParseNode(cx, ts, PN_TERNARY, tc); if (!pn) return NULL; /* * Always accept the 'in' operator in the middle clause of a ternary, * where it's unambiguous, even if we might be parsing the init of a * for statement. */ oldflags = tc->flags; tc->flags &= ~TCF_IN_FOR_INIT; pn2 = AssignExpr(cx, ts, tc); tc->flags = oldflags | (tc->flags & TCF_FUN_FLAGS); if (!pn2) return NULL; MUST_MATCH_TOKEN(TOK_COLON, JSMSG_COLON_IN_COND); pn3 = AssignExpr(cx, ts, tc); if (!pn3) return NULL; pn->pn_pos.begin = pn1->pn_pos.begin; pn->pn_pos.end = pn3->pn_pos.end; pn->pn_kid1 = pn1; pn->pn_kid2 = pn2; pn->pn_kid3 = pn3; } return pn; } static JSParseNode * OrExpr(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc) { JSParseNode *pn; pn = AndExpr(cx, ts, tc); if (pn && js_MatchToken(cx, ts, TOK_OR)) pn = NewBinary(cx, TOK_OR, JSOP_OR, pn, OrExpr(cx, ts, tc), tc); return pn; } static JSParseNode * AndExpr(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc) { JSParseNode *pn; pn = BitOrExpr(cx, ts, tc); if (pn && js_MatchToken(cx, ts, TOK_AND)) pn = NewBinary(cx, TOK_AND, JSOP_AND, pn, AndExpr(cx, ts, tc), tc); return pn; } static JSParseNode * BitOrExpr(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc) { JSParseNode *pn; pn = BitXorExpr(cx, ts, tc); while (pn && js_MatchToken(cx, ts, TOK_BITOR)) { pn = NewBinary(cx, TOK_BITOR, JSOP_BITOR, pn, BitXorExpr(cx, ts, tc), tc); } return pn; } static JSParseNode * BitXorExpr(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc) { JSParseNode *pn; pn = BitAndExpr(cx, ts, tc); while (pn && js_MatchToken(cx, ts, TOK_BITXOR)) { pn = NewBinary(cx, TOK_BITXOR, JSOP_BITXOR, pn, BitAndExpr(cx, ts, tc), tc); } return pn; } static JSParseNode * BitAndExpr(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc) { JSParseNode *pn; pn = EqExpr(cx, ts, tc); while (pn && js_MatchToken(cx, ts, TOK_BITAND)) pn = NewBinary(cx, TOK_BITAND, JSOP_BITAND, pn, EqExpr(cx, ts, tc), tc); return pn; } static JSParseNode * EqExpr(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc) { JSParseNode *pn; JSOp op; pn = RelExpr(cx, ts, tc); while (pn && js_MatchToken(cx, ts, TOK_EQOP)) { op = CURRENT_TOKEN(ts).t_op; pn = NewBinary(cx, TOK_EQOP, op, pn, RelExpr(cx, ts, tc), tc); } return pn; } static JSParseNode * RelExpr(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc) { JSParseNode *pn; JSTokenType tt; JSOp op; uintN inForInitFlag = tc->flags & TCF_IN_FOR_INIT; /* * Uses of the in operator in ShiftExprs are always unambiguous, * so unset the flag that prohibits recognizing it. */ tc->flags &= ~TCF_IN_FOR_INIT; pn = ShiftExpr(cx, ts, tc); while (pn && (js_MatchToken(cx, ts, TOK_RELOP) || /* * Recognize the 'in' token as an operator only if we're not * currently in the init expr of a for loop. */ (inForInitFlag == 0 && js_MatchToken(cx, ts, TOK_IN)) || js_MatchToken(cx, ts, TOK_INSTANCEOF))) { tt = CURRENT_TOKEN(ts).type; op = CURRENT_TOKEN(ts).t_op; pn = NewBinary(cx, tt, op, pn, ShiftExpr(cx, ts, tc), tc); } /* Restore previous state of inForInit flag. */ tc->flags |= inForInitFlag; return pn; } static JSParseNode * ShiftExpr(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc) { JSParseNode *pn; JSOp op; pn = AddExpr(cx, ts, tc); while (pn && js_MatchToken(cx, ts, TOK_SHOP)) { op = CURRENT_TOKEN(ts).t_op; pn = NewBinary(cx, TOK_SHOP, op, pn, AddExpr(cx, ts, tc), tc); } return pn; } static JSParseNode * AddExpr(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc) { JSParseNode *pn; JSTokenType tt; JSOp op; pn = MulExpr(cx, ts, tc); while (pn && (js_MatchToken(cx, ts, TOK_PLUS) || js_MatchToken(cx, ts, TOK_MINUS))) { tt = CURRENT_TOKEN(ts).type; op = (tt == TOK_PLUS) ? JSOP_ADD : JSOP_SUB; pn = NewBinary(cx, tt, op, pn, MulExpr(cx, ts, tc), tc); } return pn; } static JSParseNode * MulExpr(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc) { JSParseNode *pn; JSTokenType tt; JSOp op; pn = UnaryExpr(cx, ts, tc); while (pn && (js_MatchToken(cx, ts, TOK_STAR) || js_MatchToken(cx, ts, TOK_DIVOP))) { tt = CURRENT_TOKEN(ts).type; op = CURRENT_TOKEN(ts).t_op; pn = NewBinary(cx, tt, op, pn, UnaryExpr(cx, ts, tc), tc); } return pn; } static JSParseNode * SetLvalKid(JSContext *cx, JSTokenStream *ts, JSParseNode *pn, JSParseNode *kid, const char *name) { while (kid->pn_type == TOK_RP) kid = kid->pn_kid; if (kid->pn_type != TOK_NAME && kid->pn_type != TOK_DOT && #if JS_HAS_LVALUE_RETURN (kid->pn_type != TOK_LP || kid->pn_op != JSOP_CALL) && #endif #if JS_HAS_XML_SUPPORT (kid->pn_type != TOK_UNARYOP || kid->pn_op != JSOP_XMLNAME) && #endif kid->pn_type != TOK_LB) { js_ReportCompileErrorNumber(cx, ts, JSREPORT_TS | JSREPORT_ERROR, JSMSG_BAD_OPERAND, name); return NULL; } pn->pn_kid = kid; return kid; } static const char incop_name_str[][10] = {"increment", "decrement"}; static JSBool SetIncOpKid(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc, JSParseNode *pn, JSParseNode *kid, JSTokenType tt, JSBool preorder) { JSOp op; kid = SetLvalKid(cx, ts, pn, kid, incop_name_str[tt == TOK_DEC]); if (!kid) return JS_FALSE; switch (kid->pn_type) { case TOK_NAME: op = (tt == TOK_INC) ? (preorder ? JSOP_INCNAME : JSOP_NAMEINC) : (preorder ? JSOP_DECNAME : JSOP_NAMEDEC); if (kid->pn_atom == cx->runtime->atomState.argumentsAtom) tc->flags |= TCF_FUN_HEAVYWEIGHT; break; case TOK_DOT: op = (tt == TOK_INC) ? (preorder ? JSOP_INCPROP : JSOP_PROPINC) : (preorder ? JSOP_DECPROP : JSOP_PROPDEC); break; #if JS_HAS_LVALUE_RETURN case TOK_LP: JS_ASSERT(kid->pn_op == JSOP_CALL); kid->pn_op = JSOP_SETCALL; /* FALL THROUGH */ #endif #if JS_HAS_XML_SUPPORT case TOK_UNARYOP: if (kid->pn_op == JSOP_XMLNAME) kid->pn_op = JSOP_SETXMLNAME; /* FALL THROUGH */ #endif case TOK_LB: op = (tt == TOK_INC) ? (preorder ? JSOP_INCELEM : JSOP_ELEMINC) : (preorder ? JSOP_DECELEM : JSOP_ELEMDEC); break; default: JS_ASSERT(0); op = JSOP_NOP; } pn->pn_op = op; return JS_TRUE; } static JSParseNode * UnaryExpr(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc) { JSTokenType tt; JSParseNode *pn, *pn2; CHECK_RECURSION(); ts->flags |= TSF_OPERAND; tt = js_GetToken(cx, ts); ts->flags &= ~TSF_OPERAND; switch (tt) { case TOK_UNARYOP: case TOK_PLUS: case TOK_MINUS: pn = NewParseNode(cx, ts, PN_UNARY, tc); if (!pn) return NULL; pn->pn_type = TOK_UNARYOP; /* PLUS and MINUS are binary */ pn->pn_op = CURRENT_TOKEN(ts).t_op; pn2 = UnaryExpr(cx, ts, tc); if (!pn2) return NULL; pn->pn_pos.end = pn2->pn_pos.end; pn->pn_kid = pn2; break; case TOK_INC: case TOK_DEC: pn = NewParseNode(cx, ts, PN_UNARY, tc); if (!pn) return NULL; pn2 = MemberExpr(cx, ts, tc, JS_TRUE); if (!pn2) return NULL; if (!SetIncOpKid(cx, ts, tc, pn, pn2, tt, JS_TRUE)) return NULL; pn->pn_pos.end = pn2->pn_pos.end; break; case TOK_DELETE: pn = NewParseNode(cx, ts, PN_UNARY, tc); if (!pn) return NULL; pn2 = UnaryExpr(cx, ts, tc); if (!pn2) return NULL; pn->pn_pos.end = pn2->pn_pos.end; /* * Under ECMA3, deleting any unary expression is valid -- it simply * returns true. Here we strip off any parentheses. */ while (pn2->pn_type == TOK_RP) pn2 = pn2->pn_kid; pn->pn_kid = pn2; break; case TOK_ERROR: return NULL; default: js_UngetToken(ts); pn = MemberExpr(cx, ts, tc, JS_TRUE); if (!pn) return NULL; /* Don't look across a newline boundary for a postfix incop. */ if (ON_CURRENT_LINE(ts, pn->pn_pos)) { ts->flags |= TSF_OPERAND; tt = js_PeekTokenSameLine(cx, ts); ts->flags &= ~TSF_OPERAND; if (tt == TOK_INC || tt == TOK_DEC) { (void) js_GetToken(cx, ts); pn2 = NewParseNode(cx, ts, PN_UNARY, tc); if (!pn2) return NULL; if (!SetIncOpKid(cx, ts, tc, pn2, pn, tt, JS_FALSE)) return NULL; pn2->pn_pos.begin = pn->pn_pos.begin; pn = pn2; } } break; } return pn; } static JSBool ArgumentList(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc, JSParseNode *listNode) { JSBool matched; ts->flags |= TSF_OPERAND; matched = js_MatchToken(cx, ts, TOK_RP); ts->flags &= ~TSF_OPERAND; if (!matched) { do { JSParseNode *argNode = AssignExpr(cx, ts, tc); if (!argNode) return JS_FALSE; #if JS_HAS_GENERATORS if (argNode->pn_type == TOK_YIELD) { js_ReportCompileErrorNumber(cx, argNode, JSREPORT_PN | JSREPORT_ERROR, JSMSG_BAD_YIELD_SYNTAX); return JS_FALSE; } #endif PN_APPEND(listNode, argNode); } while (js_MatchToken(cx, ts, TOK_COMMA)); if (js_GetToken(cx, ts) != TOK_RP) { js_ReportCompileErrorNumber(cx, ts, JSREPORT_TS | JSREPORT_ERROR, JSMSG_PAREN_AFTER_ARGS); return JS_FALSE; } } return JS_TRUE; } static JSParseNode * MemberExpr(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc, JSBool allowCallSyntax) { JSParseNode *pn, *pn2, *pn3; JSTokenType tt; CHECK_RECURSION(); /* Check for new expression first. */ ts->flags |= TSF_OPERAND; tt = js_GetToken(cx, ts); ts->flags &= ~TSF_OPERAND; if (tt == TOK_NEW) { pn = NewParseNode(cx, ts, PN_LIST, tc); if (!pn) return NULL; pn2 = MemberExpr(cx, ts, tc, JS_FALSE); if (!pn2) return NULL; pn->pn_op = JSOP_NEW; PN_INIT_LIST_1(pn, pn2); pn->pn_pos.begin = pn2->pn_pos.begin; if (js_MatchToken(cx, ts, TOK_LP) && !ArgumentList(cx, ts, tc, pn)) return NULL; if (pn->pn_count > ARGC_LIMIT) { JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_TOO_MANY_CON_ARGS); return NULL; } pn->pn_pos.end = PN_LAST(pn)->pn_pos.end; } else { pn = PrimaryExpr(cx, ts, tc, tt, JS_FALSE); if (!pn) return NULL; if (pn->pn_type == TOK_ANYNAME || pn->pn_type == TOK_AT || pn->pn_type == TOK_DBLCOLON) { pn2 = NewOrRecycledNode(cx, tc); if (!pn2) return NULL; pn2->pn_type = TOK_UNARYOP; pn2->pn_pos = pn->pn_pos; pn2->pn_op = JSOP_XMLNAME; pn2->pn_arity = PN_UNARY; pn2->pn_kid = pn; pn2->pn_next = NULL; pn2->pn_ts = ts; pn2->pn_source = NULL; pn = pn2; } } while ((tt = js_GetToken(cx, ts)) > TOK_EOF) { if (tt == TOK_DOT) { pn2 = NewParseNode(cx, ts, PN_NAME, tc); if (!pn2) return NULL; pn2->pn_slot = -1; pn2->pn_attrs = 0; #if JS_HAS_XML_SUPPORT ts->flags |= TSF_OPERAND | TSF_KEYWORD_IS_NAME; tt = js_GetToken(cx, ts); ts->flags &= ~(TSF_OPERAND | TSF_KEYWORD_IS_NAME); pn3 = PrimaryExpr(cx, ts, tc, tt, JS_TRUE); if (!pn3) return NULL; tt = pn3->pn_type; if (tt == TOK_NAME || (tt == TOK_DBLCOLON && pn3->pn_arity == PN_NAME && pn3->pn_expr->pn_type == TOK_FUNCTION)) { pn2->pn_op = (tt == TOK_NAME) ? JSOP_GETPROP : JSOP_GETMETHOD; pn2->pn_expr = pn; pn2->pn_atom = pn3->pn_atom; RecycleTree(pn3, tc); } else { if (TOKEN_TYPE_IS_XML(tt)) { pn2->pn_type = TOK_LB; pn2->pn_op = JSOP_GETELEM; } else if (tt == TOK_RP) { JSParseNode *group = pn3; /* Recycle the useless TOK_RP/JSOP_GROUP node. */ pn3 = group->pn_kid; group->pn_kid = NULL; RecycleTree(group, tc); pn2->pn_type = TOK_FILTER; pn2->pn_op = JSOP_FILTER; /* A filtering predicate is like a with statement. */ tc->flags |= TCF_FUN_HEAVYWEIGHT; } else { js_ReportCompileErrorNumber(cx, ts, JSREPORT_TS | JSREPORT_ERROR, JSMSG_NAME_AFTER_DOT); return NULL; } pn2->pn_arity = PN_BINARY; pn2->pn_left = pn; pn2->pn_right = pn3; } #else ts->flags |= TSF_KEYWORD_IS_NAME; MUST_MATCH_TOKEN(TOK_NAME, JSMSG_NAME_AFTER_DOT); ts->flags &= ~TSF_KEYWORD_IS_NAME; pn2->pn_op = JSOP_GETPROP; pn2->pn_expr = pn; pn2->pn_atom = CURRENT_TOKEN(ts).t_atom; #endif pn2->pn_pos.begin = pn->pn_pos.begin; pn2->pn_pos.end = CURRENT_TOKEN(ts).pos.end; #if JS_HAS_XML_SUPPORT } else if (tt == TOK_DBLDOT) { pn2 = NewParseNode(cx, ts, PN_BINARY, tc); if (!pn2) return NULL; ts->flags |= TSF_OPERAND | TSF_KEYWORD_IS_NAME; tt = js_GetToken(cx, ts); ts->flags &= ~(TSF_OPERAND | TSF_KEYWORD_IS_NAME); pn3 = PrimaryExpr(cx, ts, tc, tt, JS_TRUE); if (!pn3) return NULL; tt = pn3->pn_type; if (tt == TOK_NAME) { pn3->pn_type = TOK_STRING; pn3->pn_arity = PN_NULLARY; pn3->pn_op = JSOP_QNAMEPART; } else if (!TOKEN_TYPE_IS_XML(tt)) { js_ReportCompileErrorNumber(cx, ts, JSREPORT_TS | JSREPORT_ERROR, JSMSG_NAME_AFTER_DOT); return NULL; } pn2->pn_op = JSOP_DESCENDANTS; pn2->pn_left = pn; pn2->pn_right = pn3; pn2->pn_pos.begin = pn->pn_pos.begin; pn2->pn_pos.end = CURRENT_TOKEN(ts).pos.end; #endif } else if (tt == TOK_LB) { pn2 = NewParseNode(cx, ts, PN_BINARY, tc); if (!pn2) return NULL; pn3 = Expr(cx, ts, tc); if (!pn3) return NULL; MUST_MATCH_TOKEN(TOK_RB, JSMSG_BRACKET_IN_INDEX); pn2->pn_pos.begin = pn->pn_pos.begin; pn2->pn_pos.end = CURRENT_TOKEN(ts).pos.end; /* Optimize o['p'] to o.p by rewriting pn2. */ if (pn3->pn_type == TOK_STRING) { pn2->pn_type = TOK_DOT; pn2->pn_op = JSOP_GETPROP; pn2->pn_arity = PN_NAME; pn2->pn_expr = pn; pn2->pn_atom = pn3->pn_atom; } else { pn2->pn_op = JSOP_GETELEM; pn2->pn_left = pn; pn2->pn_right = pn3; } } else if (allowCallSyntax && tt == TOK_LP) { pn2 = NewParseNode(cx, ts, PN_LIST, tc); if (!pn2) return NULL; /* Pick JSOP_EVAL and flag tc as heavyweight if eval(...). */ pn2->pn_op = JSOP_CALL; if (pn->pn_op == JSOP_NAME && pn->pn_atom == cx->runtime->atomState.evalAtom) { pn2->pn_op = JSOP_EVAL; tc->flags |= TCF_FUN_HEAVYWEIGHT; } PN_INIT_LIST_1(pn2, pn); pn2->pn_pos.begin = pn->pn_pos.begin; if (!ArgumentList(cx, ts, tc, pn2)) return NULL; if (pn2->pn_count > ARGC_LIMIT) { JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_TOO_MANY_FUN_ARGS); return NULL; } pn2->pn_pos.end = CURRENT_TOKEN(ts).pos.end; } else { js_UngetToken(ts); return pn; } pn = pn2; } if (tt == TOK_ERROR) return NULL; return pn; } static JSParseNode * BracketedExpr(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc) { uintN oldflags; JSParseNode *pn; /* * Always accept the 'in' operator in a parenthesized expression, * where it's unambiguous, even if we might be parsing the init of a * for statement. */ oldflags = tc->flags; tc->flags &= ~TCF_IN_FOR_INIT; pn = Expr(cx, ts, tc); tc->flags = oldflags | (tc->flags & TCF_FUN_FLAGS); return pn; } #if JS_HAS_XML_SUPPORT static JSParseNode * EndBracketedExpr(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc) { JSParseNode *pn; pn = BracketedExpr(cx, ts, tc); if (!pn) return NULL; MUST_MATCH_TOKEN(TOK_RB, JSMSG_BRACKET_AFTER_ATTR_EXPR); return pn; } /* * From the ECMA-357 grammar in 11.1.1 and 11.1.2: * * AttributeIdentifier: * @ PropertySelector * @ QualifiedIdentifier * @ [ Expression ] * * PropertySelector: * Identifier * * * * QualifiedIdentifier: * PropertySelector :: PropertySelector * PropertySelector :: [ Expression ] * * We adapt AttributeIdentifier and QualifiedIdentier to be LL(1), like so: * * AttributeIdentifier: * @ QualifiedIdentifier * @ [ Expression ] * * PropertySelector: * Identifier * * * * QualifiedIdentifier: * PropertySelector :: PropertySelector * PropertySelector :: [ Expression ] * PropertySelector * * As PrimaryExpression: Identifier is in ECMA-262 and we want the semantics * for that rule to result in a name node, but ECMA-357 extends the grammar * to include PrimaryExpression: QualifiedIdentifier, we must factor further: * * QualifiedIdentifier: * PropertySelector QualifiedSuffix * * QualifiedSuffix: * :: PropertySelector * :: [ Expression ] * /nothing/ * * And use this production instead of PrimaryExpression: QualifiedIdentifier: * * PrimaryExpression: * Identifier QualifiedSuffix * * We hoist the :: match into callers of QualifiedSuffix, in order to tweak * PropertySelector vs. Identifier pn_arity, pn_op, and other members. */ static JSParseNode * PropertySelector(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc) { JSParseNode *pn; pn = NewParseNode(cx, ts, PN_NULLARY, tc); if (!pn) return NULL; if (pn->pn_type == TOK_STAR) { pn->pn_type = TOK_ANYNAME; pn->pn_op = JSOP_ANYNAME; pn->pn_atom = cx->runtime->atomState.starAtom; } else { JS_ASSERT(pn->pn_type == TOK_NAME); pn->pn_op = JSOP_QNAMEPART; pn->pn_arity = PN_NAME; pn->pn_atom = CURRENT_TOKEN(ts).t_atom; pn->pn_expr = NULL; pn->pn_slot = -1; pn->pn_attrs = 0; } return pn; } static JSParseNode * QualifiedSuffix(JSContext *cx, JSTokenStream *ts, JSParseNode *pn, JSTreeContext *tc) { JSParseNode *pn2, *pn3; JSTokenType tt; JS_ASSERT(CURRENT_TOKEN(ts).type == TOK_DBLCOLON); pn2 = NewParseNode(cx, ts, PN_NAME, tc); if (!pn2) return NULL; /* Left operand of :: must be evaluated if it is an identifier. */ if (pn->pn_op == JSOP_QNAMEPART) pn->pn_op = JSOP_NAME; ts->flags |= TSF_KEYWORD_IS_NAME; tt = js_GetToken(cx, ts); ts->flags &= ~TSF_KEYWORD_IS_NAME; if (tt == TOK_STAR || tt == TOK_NAME) { /* Inline and specialize PropertySelector for JSOP_QNAMECONST. */ pn2->pn_op = JSOP_QNAMECONST; pn2->pn_atom = (tt == TOK_STAR) ? cx->runtime->atomState.starAtom : CURRENT_TOKEN(ts).t_atom; pn2->pn_expr = pn; pn2->pn_slot = -1; pn2->pn_attrs = 0; return pn2; } if (tt != TOK_LB) { js_ReportCompileErrorNumber(cx, ts, JSREPORT_TS | JSREPORT_ERROR, JSMSG_SYNTAX_ERROR); return NULL; } pn3 = EndBracketedExpr(cx, ts, tc); if (!pn3) return NULL; pn2->pn_op = JSOP_QNAME; pn2->pn_arity = PN_BINARY; pn2->pn_left = pn; pn2->pn_right = pn3; return pn2; } static JSParseNode * QualifiedIdentifier(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc) { JSParseNode *pn; pn = PropertySelector(cx, ts, tc); if (!pn) return NULL; if (js_MatchToken(cx, ts, TOK_DBLCOLON)) pn = QualifiedSuffix(cx, ts, pn, tc); return pn; } static JSParseNode * AttributeIdentifier(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc) { JSParseNode *pn, *pn2; JSTokenType tt; JS_ASSERT(CURRENT_TOKEN(ts).type == TOK_AT); pn = NewParseNode(cx, ts, PN_UNARY, tc); if (!pn) return NULL; pn->pn_op = JSOP_TOATTRNAME; ts->flags |= TSF_KEYWORD_IS_NAME; tt = js_GetToken(cx, ts); ts->flags &= ~TSF_KEYWORD_IS_NAME; if (tt == TOK_STAR || tt == TOK_NAME) { pn2 = QualifiedIdentifier(cx, ts, tc); } else if (tt == TOK_LB) { pn2 = EndBracketedExpr(cx, ts, tc); } else { js_ReportCompileErrorNumber(cx, ts, JSREPORT_TS | JSREPORT_ERROR, JSMSG_SYNTAX_ERROR); return NULL; } if (!pn2) return NULL; pn->pn_kid = pn2; return pn; } /* * Make a TOK_LC unary node whose pn_kid is an expression. */ static JSParseNode * XMLExpr(JSContext *cx, JSTokenStream *ts, JSBool inTag, JSTreeContext *tc) { JSParseNode *pn, *pn2; uintN oldflags; JS_ASSERT(CURRENT_TOKEN(ts).type == TOK_LC); pn = NewParseNode(cx, ts, PN_UNARY, tc); if (!pn) return NULL; /* * Turn off XML tag mode, but don't restore it after parsing this braced * expression. Instead, simply restore ts's old flags. This is required * because XMLExpr is called both from within a tag, and from within text * contained in an element, but outside of any start, end, or point tag. */ oldflags = ts->flags; ts->flags = oldflags & ~TSF_XMLTAGMODE; pn2 = Expr(cx, ts, tc); if (!pn2) return NULL; MUST_MATCH_TOKEN(TOK_RC, JSMSG_CURLY_IN_XML_EXPR); ts->flags = oldflags; pn->pn_kid = pn2; pn->pn_op = inTag ? JSOP_XMLTAGEXPR : JSOP_XMLELTEXPR; return pn; } /* * Make a terminal node for one of TOK_XMLNAME, TOK_XMLATTR, TOK_XMLSPACE, * TOK_XMLTEXT, TOK_XMLCDATA, TOK_XMLCOMMENT, or TOK_XMLPI. When converting * parse tree to XML, we preserve a TOK_XMLSPACE node only if it's the sole * child of a container tag. */ static JSParseNode * XMLAtomNode(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc) { JSParseNode *pn; JSToken *tp; pn = NewParseNode(cx, ts, PN_NULLARY, tc); if (!pn) return NULL; tp = &CURRENT_TOKEN(ts); pn->pn_op = tp->t_op; pn->pn_atom = tp->t_atom; if (tp->type == TOK_XMLPI) pn->pn_atom2 = tp->t_atom2; return pn; } /* * Parse the productions: * * XMLNameExpr: * XMLName XMLNameExpr? * { Expr } XMLNameExpr? * * Return a PN_LIST, PN_UNARY, or PN_NULLARY according as XMLNameExpr produces * a list of names and/or expressions, a single expression, or a single name. * If PN_LIST or PN_NULLARY, pn_type will be TOK_XMLNAME; if PN_UNARY, pn_type * will be TOK_LC. */ static JSParseNode * XMLNameExpr(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc) { JSParseNode *pn, *pn2, *list; JSTokenType tt; pn = list = NULL; do { tt = CURRENT_TOKEN(ts).type; if (tt == TOK_LC) { pn2 = XMLExpr(cx, ts, JS_TRUE, tc); if (!pn2) return NULL; } else { JS_ASSERT(tt == TOK_XMLNAME); pn2 = XMLAtomNode(cx, ts, tc); if (!pn2) return NULL; } if (!pn) { pn = pn2; } else { if (!list) { list = NewParseNode(cx, ts, PN_LIST, tc); if (!list) return NULL; list->pn_type = TOK_XMLNAME; list->pn_pos.begin = pn->pn_pos.begin; PN_INIT_LIST_1(list, pn); list->pn_extra = PNX_CANTFOLD; pn = list; } pn->pn_pos.end = pn2->pn_pos.end; PN_APPEND(pn, pn2); } } while ((tt = js_GetToken(cx, ts)) == TOK_XMLNAME || tt == TOK_LC); js_UngetToken(ts); return pn; } /* * Macro to test whether an XMLNameExpr or XMLTagContent node can be folded * at compile time into a JSXML tree. */ #define XML_FOLDABLE(pn) ((pn)->pn_arity == PN_LIST \ ? ((pn)->pn_extra & PNX_CANTFOLD) == 0 \ : (pn)->pn_type != TOK_LC) /* * Parse the productions: * * XMLTagContent: * XMLNameExpr * XMLTagContent S XMLNameExpr S? = S? XMLAttr * XMLTagContent S XMLNameExpr S? = S? { Expr } * * Return a PN_LIST, PN_UNARY, or PN_NULLARY according to how XMLTagContent * produces a list of name and attribute values and/or braced expressions, a * single expression, or a single name. * * If PN_LIST or PN_NULLARY, pn_type will be TOK_XMLNAME for the case where * XMLTagContent: XMLNameExpr. If pn_type is not TOK_XMLNAME but pn_arity is * PN_LIST, pn_type will be tagtype. If PN_UNARY, pn_type will be TOK_LC and * we parsed exactly one expression. */ static JSParseNode * XMLTagContent(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc, JSTokenType tagtype, JSAtom **namep) { JSParseNode *pn, *pn2, *list; JSTokenType tt; pn = XMLNameExpr(cx, ts, tc); if (!pn) return NULL; *namep = (pn->pn_arity == PN_NULLARY) ? pn->pn_atom : NULL; list = NULL; while (js_MatchToken(cx, ts, TOK_XMLSPACE)) { tt = js_GetToken(cx, ts); if (tt != TOK_XMLNAME && tt != TOK_LC) { js_UngetToken(ts); break; } pn2 = XMLNameExpr(cx, ts, tc); if (!pn2) return NULL; if (!list) { list = NewParseNode(cx, ts, PN_LIST, tc); if (!list) return NULL; list->pn_type = tagtype; list->pn_pos.begin = pn->pn_pos.begin; PN_INIT_LIST_1(list, pn); pn = list; } PN_APPEND(pn, pn2); if (!XML_FOLDABLE(pn2)) pn->pn_extra |= PNX_CANTFOLD; js_MatchToken(cx, ts, TOK_XMLSPACE); MUST_MATCH_TOKEN(TOK_ASSIGN, JSMSG_NO_ASSIGN_IN_XML_ATTR); js_MatchToken(cx, ts, TOK_XMLSPACE); tt = js_GetToken(cx, ts); if (tt == TOK_XMLATTR) { pn2 = XMLAtomNode(cx, ts, tc); } else if (tt == TOK_LC) { pn2 = XMLExpr(cx, ts, JS_TRUE, tc); pn->pn_extra |= PNX_CANTFOLD; } else { js_ReportCompileErrorNumber(cx, ts, JSREPORT_TS | JSREPORT_ERROR, JSMSG_BAD_XML_ATTR_VALUE); return NULL; } if (!pn2) return NULL; pn->pn_pos.end = pn2->pn_pos.end; PN_APPEND(pn, pn2); } return pn; } #define XML_CHECK_FOR_ERROR_AND_EOF(tt,result) \ JS_BEGIN_MACRO \ if ((tt) <= TOK_EOF) { \ if ((tt) == TOK_EOF) { \ js_ReportCompileErrorNumber(cx, ts, \ JSREPORT_TS | JSREPORT_ERROR, \ JSMSG_END_OF_XML_SOURCE); \ } \ return result; \ } \ JS_END_MACRO static JSParseNode * XMLElementOrList(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc, JSBool allowList); /* * Consume XML element tag content, including the TOK_XMLETAGO (flags &= ~TSF_XMLTAGMODE; for (;;) { ts->flags |= TSF_XMLTEXTMODE; tt = js_GetToken(cx, ts); ts->flags &= ~TSF_XMLTEXTMODE; XML_CHECK_FOR_ERROR_AND_EOF(tt, JS_FALSE); JS_ASSERT(tt == TOK_XMLSPACE || tt == TOK_XMLTEXT); textAtom = CURRENT_TOKEN(ts).t_atom; if (textAtom) { /* Non-zero-length XML text scanned. */ pn2 = XMLAtomNode(cx, ts, tc); if (!pn2) return JS_FALSE; pn->pn_pos.end = pn2->pn_pos.end; PN_APPEND(pn, pn2); } ts->flags |= TSF_OPERAND; tt = js_GetToken(cx, ts); ts->flags &= ~TSF_OPERAND; XML_CHECK_FOR_ERROR_AND_EOF(tt, JS_FALSE); if (tt == TOK_XMLETAGO) break; if (tt == TOK_LC) { pn2 = XMLExpr(cx, ts, JS_FALSE, tc); pn->pn_extra |= PNX_CANTFOLD; } else if (tt == TOK_XMLSTAGO) { pn2 = XMLElementOrList(cx, ts, tc, JS_FALSE); if (pn2) { pn2->pn_extra &= ~PNX_XMLROOT; pn->pn_extra |= pn2->pn_extra; } } else { JS_ASSERT(tt == TOK_XMLCDATA || tt == TOK_XMLCOMMENT || tt == TOK_XMLPI); pn2 = XMLAtomNode(cx, ts, tc); } if (!pn2) return JS_FALSE; pn->pn_pos.end = pn2->pn_pos.end; PN_APPEND(pn, pn2); } JS_ASSERT(CURRENT_TOKEN(ts).type == TOK_XMLETAGO); ts->flags |= TSF_XMLTAGMODE; return JS_TRUE; } /* * Return a PN_LIST node containing an XML or XMLList Initialiser. */ static JSParseNode * XMLElementOrList(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc, JSBool allowList) { JSParseNode *pn, *pn2, *list; JSBool hadSpace; JSTokenType tt; JSAtom *startAtom, *endAtom; CHECK_RECURSION(); JS_ASSERT(CURRENT_TOKEN(ts).type == TOK_XMLSTAGO); pn = NewParseNode(cx, ts, PN_LIST, tc); if (!pn) return NULL; ts->flags |= TSF_XMLTAGMODE; hadSpace = js_MatchToken(cx, ts, TOK_XMLSPACE); tt = js_GetToken(cx, ts); if (tt == TOK_ERROR) return NULL; if (tt == TOK_XMLNAME || tt == TOK_LC) { /* * XMLElement. Append the tag and its contents, if any, to pn. */ pn2 = XMLTagContent(cx, ts, tc, TOK_XMLSTAGO, &startAtom); if (!pn2) return NULL; js_MatchToken(cx, ts, TOK_XMLSPACE); tt = js_GetToken(cx, ts); if (tt == TOK_XMLPTAGC) { /* Point tag (/>): recycle pn if pn2 is a list of tag contents. */ if (pn2->pn_type == TOK_XMLSTAGO) { PN_INIT_LIST(pn); RecycleTree(pn, tc); pn = pn2; } else { JS_ASSERT(pn2->pn_type == TOK_XMLNAME || pn2->pn_type == TOK_LC); PN_INIT_LIST_1(pn, pn2); if (!XML_FOLDABLE(pn2)) pn->pn_extra |= PNX_CANTFOLD; } pn->pn_type = TOK_XMLPTAGC; pn->pn_extra |= PNX_XMLROOT; } else { /* We had better have a tag-close (>) at this point. */ if (tt != TOK_XMLTAGC) { js_ReportCompileErrorNumber(cx, ts, JSREPORT_TS | JSREPORT_ERROR, JSMSG_BAD_XML_TAG_SYNTAX); return NULL; } pn2->pn_pos.end = CURRENT_TOKEN(ts).pos.end; /* Make sure pn2 is a TOK_XMLSTAGO list containing tag contents. */ if (pn2->pn_type != TOK_XMLSTAGO) { PN_INIT_LIST_1(pn, pn2); if (!XML_FOLDABLE(pn2)) pn->pn_extra |= PNX_CANTFOLD; pn2 = pn; pn = NewParseNode(cx, ts, PN_LIST, tc); if (!pn) return NULL; } /* Now make pn a nominal-root TOK_XMLELEM list containing pn2. */ pn->pn_type = TOK_XMLELEM; PN_INIT_LIST_1(pn, pn2); if (!XML_FOLDABLE(pn2)) pn->pn_extra |= PNX_CANTFOLD; pn->pn_extra |= PNX_XMLROOT; /* Get element contents and delimiting end-tag-open sequence. */ if (!XMLElementContent(cx, ts, pn, tc)) return NULL; js_MatchToken(cx, ts, TOK_XMLSPACE); tt = js_GetToken(cx, ts); XML_CHECK_FOR_ERROR_AND_EOF(tt, NULL); if (tt != TOK_XMLNAME && tt != TOK_LC) { js_ReportCompileErrorNumber(cx, ts, JSREPORT_TS | JSREPORT_ERROR, JSMSG_BAD_XML_TAG_SYNTAX); return NULL; } /* Parse end tag; check mismatch at compile-time if we can. */ pn2 = XMLTagContent(cx, ts, tc, TOK_XMLETAGO, &endAtom); if (!pn2) return NULL; if (pn2->pn_type == TOK_XMLETAGO) { /* Oops, end tag has attributes! */ js_ReportCompileErrorNumber(cx, ts, JSREPORT_TS | JSREPORT_ERROR, JSMSG_BAD_XML_TAG_SYNTAX); return NULL; } if (endAtom && startAtom && endAtom != startAtom) { JSString *str = ATOM_TO_STRING(startAtom); /* End vs. start tag name mismatch: point to the tag name. */ js_ReportCompileErrorNumberUC(cx, pn2, JSREPORT_PN | JSREPORT_ERROR, JSMSG_XML_TAG_NAME_MISMATCH, JSSTRING_CHARS(str)); return NULL; } /* Make a TOK_XMLETAGO list with pn2 as its single child. */ JS_ASSERT(pn2->pn_type == TOK_XMLNAME || pn2->pn_type == TOK_LC); list = NewParseNode(cx, ts, PN_LIST, tc); if (!list) return NULL; list->pn_type = TOK_XMLETAGO; PN_INIT_LIST_1(list, pn2); PN_APPEND(pn, list); if (!XML_FOLDABLE(pn2)) { list->pn_extra |= PNX_CANTFOLD; pn->pn_extra |= PNX_CANTFOLD; } js_MatchToken(cx, ts, TOK_XMLSPACE); MUST_MATCH_TOKEN(TOK_XMLTAGC, JSMSG_BAD_XML_TAG_SYNTAX); } /* Set pn_op now that pn has been updated to its final value. */ pn->pn_op = JSOP_TOXML; } else if (!hadSpace && allowList && tt == TOK_XMLTAGC) { /* XMLList Initialiser. */ pn->pn_type = TOK_XMLLIST; pn->pn_op = JSOP_TOXMLLIST; PN_INIT_LIST(pn); pn->pn_extra |= PNX_XMLROOT; if (!XMLElementContent(cx, ts, pn, tc)) return NULL; MUST_MATCH_TOKEN(TOK_XMLTAGC, JSMSG_BAD_XML_LIST_SYNTAX); } else { js_ReportCompileErrorNumber(cx, ts, JSREPORT_TS | JSREPORT_ERROR, JSMSG_BAD_XML_NAME_SYNTAX); return NULL; } pn->pn_pos.end = CURRENT_TOKEN(ts).pos.end; ts->flags &= ~TSF_XMLTAGMODE; return pn; } static JSParseNode * XMLElementOrListRoot(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc, JSBool allowList) { uint32 oldopts; JSParseNode *pn; /* * Force XML support to be enabled so that comments and CDATA literals * are recognized, instead of ). */ oldopts = JS_SetOptions(cx, cx->options | JSOPTION_XML); pn = XMLElementOrList(cx, ts, tc, allowList); JS_SetOptions(cx, oldopts); return pn; } JS_FRIEND_API(JSParseNode *) js_ParseXMLTokenStream(JSContext *cx, JSObject *chain, JSTokenStream *ts, JSBool allowList) { JSStackFrame *fp, frame; JSParseNode *pn; JSTreeContext tc; JSTokenType tt; /* * Push a compiler frame if we have no frames, or if the top frame is a * lightweight function activation, or if its scope chain doesn't match * the one passed to us. */ fp = cx->fp; MaybeSetupFrame(cx, chain, fp, &frame); JS_KEEP_ATOMS(cx->runtime); TREE_CONTEXT_INIT(&tc); /* Set XML-only mode to turn off special treatment of {expr} in XML. */ ts->flags |= TSF_OPERAND | TSF_XMLONLYMODE; tt = js_GetToken(cx, ts); ts->flags &= ~TSF_OPERAND; if (tt != TOK_XMLSTAGO) { js_ReportCompileErrorNumber(cx, ts, JSREPORT_TS | JSREPORT_ERROR, JSMSG_BAD_XML_MARKUP); pn = NULL; } else { pn = XMLElementOrListRoot(cx, ts, &tc, allowList); } ts->flags &= ~TSF_XMLONLYMODE; TREE_CONTEXT_FINISH(&tc); JS_UNKEEP_ATOMS(cx->runtime); cx->fp = fp; return pn; } #endif /* JS_HAS_XMLSUPPORT */ static JSParseNode * PrimaryExpr(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc, JSTokenType tt, JSBool afterDot) { JSParseNode *pn, *pn2, *pn3; JSOp op; #if JS_HAS_SHARP_VARS JSParseNode *defsharp; JSBool notsharp; defsharp = NULL; notsharp = JS_FALSE; again: /* * Control flows here after #n= is scanned. If the following primary is * not valid after such a "sharp variable" definition, the tt switch case * should set notsharp. */ #endif CHECK_RECURSION(); #if JS_HAS_GETTER_SETTER if (tt == TOK_NAME) { tt = CheckGetterOrSetter(cx, ts, TOK_FUNCTION); if (tt == TOK_ERROR) return NULL; } #endif switch (tt) { case TOK_FUNCTION: #if JS_HAS_XML_SUPPORT ts->flags |= TSF_KEYWORD_IS_NAME; if (js_MatchToken(cx, ts, TOK_DBLCOLON)) { ts->flags &= ~TSF_KEYWORD_IS_NAME; pn2 = NewParseNode(cx, ts, PN_NULLARY, tc); if (!pn2) return NULL; pn2->pn_type = TOK_FUNCTION; pn = QualifiedSuffix(cx, ts, pn2, tc); if (!pn) return NULL; break; } ts->flags &= ~TSF_KEYWORD_IS_NAME; #endif pn = FunctionExpr(cx, ts, tc); if (!pn) return NULL; break; case TOK_LB: { JSBool matched; jsuint index; pn = NewParseNode(cx, ts, PN_LIST, tc); if (!pn) return NULL; pn->pn_type = TOK_RB; #if JS_HAS_SHARP_VARS if (defsharp) { PN_INIT_LIST_1(pn, defsharp); defsharp = NULL; } else #endif PN_INIT_LIST(pn); ts->flags |= TSF_OPERAND; matched = js_MatchToken(cx, ts, TOK_RB); ts->flags &= ~TSF_OPERAND; if (!matched) { for (index = 0; ; index++) { if (index == ARRAY_INIT_LIMIT) { js_ReportCompileErrorNumber(cx, ts, JSREPORT_TS | JSREPORT_ERROR, JSMSG_ARRAY_INIT_TOO_BIG); return NULL; } ts->flags |= TSF_OPERAND; tt = js_PeekToken(cx, ts); ts->flags &= ~TSF_OPERAND; if (tt == TOK_RB) { pn->pn_extra |= PNX_ENDCOMMA; break; } if (tt == TOK_COMMA) { /* So CURRENT_TOKEN gets TOK_COMMA and not TOK_LB. */ js_MatchToken(cx, ts, TOK_COMMA); pn2 = NewParseNode(cx, ts, PN_NULLARY, tc); } else { pn2 = AssignExpr(cx, ts, tc); } if (!pn2) return NULL; PN_APPEND(pn, pn2); if (tt != TOK_COMMA) { /* If we didn't already match TOK_COMMA in above case. */ if (!js_MatchToken(cx, ts, TOK_COMMA)) break; } } #if JS_HAS_GENERATORS /* * At this point, (index == 0 && pn->pn_count != 0) implies one * element initialiser was parsed (possibly with a defsharp before * the left bracket). * * An array comprehension of the form: * * [i * j for (i in o) for (j in p) if (i != j)] * * translates to roughly the following let expression: * * let (array = new Array, i, j) { * for (i in o) let { * for (j in p) * if (i != j) * array.push(i * j) * } * array * } * * where array is a nameless block-local variable. The "roughly" * means that an implementation may optimize away the array.push. * An array comprehension opens exactly one block scope, no matter * how many for heads it contains. * * Each let () {...} or for (let ...) ... compiles to: * * JSOP_ENTERBLOCK ... JSOP_LEAVEBLOCK * * where is a literal object representing the block scope, * with properties, naming each var declared in the block. * * Each var declaration in a let-block binds a name in at * compile time, and allocates a slot on the operand stack at * runtime via JSOP_ENTERBLOCK. A block-local var is accessed * by the JSOP_GETLOCAL and JSOP_SETLOCAL ops, and iterated with * JSOP_FORLOCAL. These ops all have an immediate operand, the * local slot's stack index from fp->spbase. * * The array comprehension iteration step, array.push(i * j) in * the example above, is done by ; JSOP_ARRAYCOMP , * where is the index of array's stack slot. */ if (index == 0 && pn->pn_count != 0 && js_MatchToken(cx, ts, TOK_FOR)) { JSParseNode **pnp, *pnexp, *pntop, *pnlet; BindData data; JSRuntime *rt; JSStmtInfo stmtInfo; JSAtom *atom; /* Relabel pn as an array comprehension node. */ pn->pn_type = TOK_ARRAYCOMP; /* * Remove the comprehension expression from pn's linked list * and save it via pnexp. We'll re-install it underneath the * ARRAYPUSH node after we parse the rest of the comprehension. */ pnexp = PN_LAST(pn); JS_ASSERT(pn->pn_count == 1 || pn->pn_count == 2); pn->pn_tail = (--pn->pn_count == 1) ? &pn->pn_head->pn_next : &pn->pn_head; *pn->pn_tail = NULL; /* * Make a parse-node and literal object representing the array * comprehension's block scope. */ pntop = PushLexicalScope(cx, ts, tc, &stmtInfo); if (!pntop) return NULL; pnp = &pntop->pn_expr; data.pn = NULL; data.ts = ts; data.obj = tc->blockChain; data.op = JSOP_NOP; data.binder = BindLet; data.u.let.index = 0; data.u.let.overflow = JSMSG_ARRAY_INIT_TOO_BIG; rt = cx->runtime; do { /* * FOR node is binary, left is control and right is body. * Use index to count each block-local let-variable on the * left-hand side of IN. */ pn2 = NewParseNode(cx, ts, PN_BINARY, tc); if (!pn2) return NULL; pn2->pn_op = JSOP_FORIN; if (js_MatchToken(cx, ts, TOK_NAME)) { if (CURRENT_TOKEN(ts).t_atom == rt->atomState.eachAtom) pn2->pn_op = JSOP_FOREACH; else js_UngetToken(ts); } MUST_MATCH_TOKEN(TOK_LP, JSMSG_PAREN_AFTER_FOR); tt = js_GetToken(cx, ts); switch (tt) { #if JS_HAS_DESTRUCTURING case TOK_LB: case TOK_LC: pnlet = DestructuringExpr(cx, &data, tc, tt); if (!pnlet) return NULL; if (pnlet->pn_type != TOK_RB || pnlet->pn_count != 2) { js_ReportCompileErrorNumber(cx, ts, JSREPORT_TS | JSREPORT_ERROR, JSMSG_BAD_FOR_LEFTSIDE); return NULL; } /* Destructuring requires [key, value] enumeration. */ if (pn2->pn_op != JSOP_FOREACH) pn2->pn_op = JSOP_FOREACHKEYVAL; break; #endif case TOK_NAME: atom = CURRENT_TOKEN(ts).t_atom; if (!data.binder(cx, &data, atom, tc)) return NULL; /* * Create a name node with op JSOP_NAME. We can't set * op to JSOP_GETLOCAL here, because we don't yet know * the block's depth in the operand stack frame. The * code generator computes that, and it tries to bind * all names to slots, so we must let it do the deed. */ pnlet = NewParseNode(cx, ts, PN_NAME, tc); if (!pnlet) return NULL; pnlet->pn_op = JSOP_NAME; pnlet->pn_atom = atom; pnlet->pn_expr = NULL; pnlet->pn_slot = -1; pnlet->pn_attrs = 0; break; default: js_ReportCompileErrorNumber(cx, ts, JSREPORT_TS|JSREPORT_ERROR, JSMSG_NO_VARIABLE_NAME); return NULL; } MUST_MATCH_TOKEN(TOK_IN, JSMSG_IN_AFTER_FOR_NAME); pn3 = NewBinary(cx, TOK_IN, JSOP_NOP, pnlet, Expr(cx, ts, tc), tc); if (!pn3) return NULL; MUST_MATCH_TOKEN(TOK_RP, JSMSG_PAREN_AFTER_FOR_CTRL); pn2->pn_left = pn3; *pnp = pn2; pnp = &pn2->pn_right; } while (js_MatchToken(cx, ts, TOK_FOR)); if (js_MatchToken(cx, ts, TOK_IF)) { pn2 = NewParseNode(cx, ts, PN_TERNARY, tc); if (!pn2) return NULL; pn2->pn_kid1 = Condition(cx, ts, tc); if (!pn2->pn_kid1) return NULL; pn2->pn_kid2 = NULL; pn2->pn_kid3 = NULL; *pnp = pn2; pnp = &pn2->pn_kid2; } pn2 = NewParseNode(cx, ts, PN_UNARY, tc); if (!pn2) return NULL; pn2->pn_type = TOK_ARRAYPUSH; pn2->pn_op = JSOP_ARRAYPUSH; pn2->pn_kid = pnexp; *pnp = pn2; PN_APPEND(pn, pntop); js_PopStatement(tc); } #endif /* JS_HAS_GENERATORS */ MUST_MATCH_TOKEN(TOK_RB, JSMSG_BRACKET_AFTER_LIST); } pn->pn_pos.end = CURRENT_TOKEN(ts).pos.end; return pn; } #if JS_HAS_BLOCK_SCOPE case TOK_LET: pn = LetBlock(cx, ts, tc, JS_FALSE); if (!pn) return NULL; break; #endif case TOK_LC: { JSBool afterComma; pn = NewParseNode(cx, ts, PN_LIST, tc); if (!pn) return NULL; pn->pn_type = TOK_RC; #if JS_HAS_SHARP_VARS if (defsharp) { PN_INIT_LIST_1(pn, defsharp); defsharp = NULL; } else #endif PN_INIT_LIST(pn); afterComma = JS_FALSE; for (;;) { ts->flags |= TSF_KEYWORD_IS_NAME; tt = js_GetToken(cx, ts); ts->flags &= ~TSF_KEYWORD_IS_NAME; switch (tt) { case TOK_NUMBER: pn3 = NewParseNode(cx, ts, PN_NULLARY, tc); if (pn3) pn3->pn_dval = CURRENT_TOKEN(ts).t_dval; break; case TOK_NAME: #if JS_HAS_GETTER_SETTER { JSAtom *atom; JSRuntime *rt; atom = CURRENT_TOKEN(ts).t_atom; rt = cx->runtime; if (atom == rt->atomState.getAtom || atom == rt->atomState.setAtom) { op = (atom == rt->atomState.getAtom) ? JSOP_GETTER : JSOP_SETTER; if (js_MatchToken(cx, ts, TOK_NAME)) { pn3 = NewParseNode(cx, ts, PN_NAME, tc); if (!pn3) return NULL; pn3->pn_atom = CURRENT_TOKEN(ts).t_atom; pn3->pn_expr = NULL; pn3->pn_slot = -1; pn3->pn_attrs = 0; /* We have to fake a 'function' token here. */ CURRENT_TOKEN(ts).t_op = JSOP_NOP; CURRENT_TOKEN(ts).type = TOK_FUNCTION; pn2 = FunctionExpr(cx, ts, tc); pn2 = NewBinary(cx, TOK_COLON, op, pn3, pn2, tc); goto skip; } } /* else fall thru ... */ } #endif case TOK_STRING: pn3 = NewParseNode(cx, ts, PN_NULLARY, tc); if (pn3) pn3->pn_atom = CURRENT_TOKEN(ts).t_atom; break; case TOK_RC: if (afterComma && !js_ReportCompileErrorNumber(cx, ts, JSREPORT_TS | JSREPORT_WARNING | JSREPORT_STRICT, JSMSG_TRAILING_COMMA)) { return NULL; } goto end_obj_init; default: js_ReportCompileErrorNumber(cx, ts, JSREPORT_TS | JSREPORT_ERROR, JSMSG_BAD_PROP_ID); return NULL; } tt = js_GetToken(cx, ts); #if JS_HAS_GETTER_SETTER if (tt == TOK_NAME) { tt = CheckGetterOrSetter(cx, ts, TOK_COLON); if (tt == TOK_ERROR) return NULL; } #endif if (tt != TOK_COLON) { js_ReportCompileErrorNumber(cx, ts, JSREPORT_TS | JSREPORT_ERROR, JSMSG_COLON_AFTER_ID); return NULL; } op = CURRENT_TOKEN(ts).t_op; pn2 = NewBinary(cx, TOK_COLON, op, pn3, AssignExpr(cx, ts, tc), tc); #if JS_HAS_GETTER_SETTER skip: #endif if (!pn2) return NULL; PN_APPEND(pn, pn2); tt = js_GetToken(cx, ts); if (tt == TOK_RC) goto end_obj_init; if (tt != TOK_COMMA) { js_ReportCompileErrorNumber(cx, ts, JSREPORT_TS | JSREPORT_ERROR, JSMSG_CURLY_AFTER_LIST); return NULL; } afterComma = JS_TRUE; } end_obj_init: pn->pn_pos.end = CURRENT_TOKEN(ts).pos.end; return pn; } #if JS_HAS_SHARP_VARS case TOK_DEFSHARP: if (defsharp) goto badsharp; defsharp = NewParseNode(cx, ts, PN_UNARY, tc); if (!defsharp) return NULL; defsharp->pn_kid = NULL; defsharp->pn_num = (jsint) CURRENT_TOKEN(ts).t_dval; ts->flags |= TSF_OPERAND; tt = js_GetToken(cx, ts); ts->flags &= ~TSF_OPERAND; goto again; case TOK_USESHARP: /* Check for forward/dangling references at runtime, to allow eval. */ pn = NewParseNode(cx, ts, PN_NULLARY, tc); if (!pn) return NULL; pn->pn_num = (jsint) CURRENT_TOKEN(ts).t_dval; notsharp = JS_TRUE; break; #endif /* JS_HAS_SHARP_VARS */ case TOK_LP: pn = NewParseNode(cx, ts, PN_UNARY, tc); if (!pn) return NULL; pn2 = BracketedExpr(cx, ts, tc); if (!pn2) return NULL; MUST_MATCH_TOKEN(TOK_RP, JSMSG_PAREN_IN_PAREN); if (pn2->pn_type == TOK_RP || (js_CodeSpec[pn2->pn_op].prec >= js_CodeSpec[JSOP_GETPROP].prec && !afterDot)) { /* * Avoid redundant JSOP_GROUP opcodes, for efficiency and mainly * to help the decompiler look ahead from a JSOP_ENDINIT to see a * JSOP_GROUP followed by a POP or POPV. That sequence means the * parentheses are mandatory, to disambiguate object initialisers * as expression statements from block statements. * * Also drop pn if pn2 is a member or a primary expression of any * kind. This is required to avoid generating a JSOP_GROUP that * will null the |obj| interpreter register, causing |this| in any * call of that member expression to bind to the global object. */ pn->pn_kid = NULL; RecycleTree(pn, tc); pn = pn2; } else { pn->pn_type = TOK_RP; pn->pn_pos.end = CURRENT_TOKEN(ts).pos.end; pn->pn_kid = pn2; } break; #if JS_HAS_XML_SUPPORT case TOK_STAR: pn = QualifiedIdentifier(cx, ts, tc); if (!pn) return NULL; notsharp = JS_TRUE; break; case TOK_AT: pn = AttributeIdentifier(cx, ts, tc); if (!pn) return NULL; notsharp = JS_TRUE; break; case TOK_XMLSTAGO: pn = XMLElementOrListRoot(cx, ts, tc, JS_TRUE); if (!pn) return NULL; notsharp = JS_TRUE; /* XXXbe could be sharp? */ break; #endif /* JS_HAS_XML_SUPPORT */ case TOK_STRING: #if JS_HAS_SHARP_VARS notsharp = JS_TRUE; /* FALL THROUGH */ #endif #if JS_HAS_XML_SUPPORT case TOK_XMLCDATA: case TOK_XMLCOMMENT: case TOK_XMLPI: #endif case TOK_NAME: case TOK_OBJECT: pn = NewParseNode(cx, ts, PN_NULLARY, tc); if (!pn) return NULL; pn->pn_atom = CURRENT_TOKEN(ts).t_atom; #if JS_HAS_XML_SUPPORT if (tt == TOK_XMLPI) pn->pn_atom2 = CURRENT_TOKEN(ts).t_atom2; else #endif pn->pn_op = CURRENT_TOKEN(ts).t_op; if (tt == TOK_NAME) { pn->pn_arity = PN_NAME; pn->pn_expr = NULL; pn->pn_slot = -1; pn->pn_attrs = 0; #if JS_HAS_XML_SUPPORT if (js_MatchToken(cx, ts, TOK_DBLCOLON)) { if (afterDot) { JSString *str; /* * Here PrimaryExpr is called after '.' or '..' and we * just scanned .name:: or ..name:: . This is the only * case where a keyword after '.' or '..' is not * treated as a property name. */ str = ATOM_TO_STRING(pn->pn_atom); tt = js_CheckKeyword(JSSTRING_CHARS(str), JSSTRING_LENGTH(str)); if (tt == TOK_FUNCTION) { pn->pn_arity = PN_NULLARY; pn->pn_type = TOK_FUNCTION; } else if (tt != TOK_EOF) { js_ReportCompileErrorNumber( cx, ts, JSREPORT_TS | JSREPORT_ERROR, JSMSG_KEYWORD_NOT_NS); return NULL; } } pn = QualifiedSuffix(cx, ts, pn, tc); if (!pn) return NULL; break; } #endif /* Unqualified __parent__ and __proto__ uses require activations. */ if (pn->pn_atom == cx->runtime->atomState.parentAtom || pn->pn_atom == cx->runtime->atomState.protoAtom) { tc->flags |= TCF_FUN_HEAVYWEIGHT; } else { JSAtomListElement *ale; JSStackFrame *fp; JSBool loopy; /* Measure optimizable global variable uses. */ ATOM_LIST_SEARCH(ale, &tc->decls, pn->pn_atom); if (ale && !(fp = cx->fp)->fun && fp->scopeChain == fp->varobj && js_IsGlobalReference(tc, pn->pn_atom, &loopy)) { tc->globalUses++; if (loopy) tc->loopyGlobalUses++; } } } break; case TOK_NUMBER: pn = NewParseNode(cx, ts, PN_NULLARY, tc); if (!pn) return NULL; pn->pn_dval = CURRENT_TOKEN(ts).t_dval; #if JS_HAS_SHARP_VARS notsharp = JS_TRUE; #endif break; case TOK_PRIMARY: pn = NewParseNode(cx, ts, PN_NULLARY, tc); if (!pn) return NULL; pn->pn_op = CURRENT_TOKEN(ts).t_op; #if JS_HAS_SHARP_VARS notsharp = JS_TRUE; #endif break; #if !JS_HAS_EXPORT_IMPORT case TOK_EXPORT: case TOK_IMPORT: #endif case TOK_ERROR: /* The scanner or one of its subroutines reported the error. */ return NULL; default: js_ReportCompileErrorNumber(cx, ts, JSREPORT_TS | JSREPORT_ERROR, JSMSG_SYNTAX_ERROR); return NULL; } #if JS_HAS_SHARP_VARS if (defsharp) { if (notsharp) { badsharp: js_ReportCompileErrorNumber(cx, ts, JSREPORT_TS | JSREPORT_ERROR, JSMSG_BAD_SHARP_VAR_DEF); return NULL; } defsharp->pn_kid = pn; return defsharp; } #endif return pn; } /* * Fold from one constant type to another. * XXX handles only strings and numbers for now */ static JSBool FoldType(JSContext *cx, JSParseNode *pn, JSTokenType type) { if (pn->pn_type != type) { switch (type) { case TOK_NUMBER: if (pn->pn_type == TOK_STRING) { jsdouble d; if (!js_ValueToNumber(cx, ATOM_KEY(pn->pn_atom), &d)) return JS_FALSE; pn->pn_dval = d; pn->pn_type = TOK_NUMBER; pn->pn_op = JSOP_NUMBER; } break; case TOK_STRING: if (pn->pn_type == TOK_NUMBER) { JSString *str = js_NumberToString(cx, pn->pn_dval); if (!str) return JS_FALSE; pn->pn_atom = js_AtomizeString(cx, str, 0); if (!pn->pn_atom) return JS_FALSE; pn->pn_type = TOK_STRING; pn->pn_op = JSOP_STRING; } break; default:; } } return JS_TRUE; } /* * Fold two numeric constants. Beware that pn1 and pn2 are recycled, unless * one of them aliases pn, so you can't safely fetch pn2->pn_next, e.g., after * a successful call to this function. */ static JSBool FoldBinaryNumeric(JSContext *cx, JSOp op, JSParseNode *pn1, JSParseNode *pn2, JSParseNode *pn, JSTreeContext *tc) { jsdouble d, d2; int32 i, j; uint32 u; JS_ASSERT(pn1->pn_type == TOK_NUMBER && pn2->pn_type == TOK_NUMBER); d = pn1->pn_dval; d2 = pn2->pn_dval; switch (op) { case JSOP_LSH: case JSOP_RSH: if (!js_DoubleToECMAInt32(cx, d, &i)) return JS_FALSE; if (!js_DoubleToECMAInt32(cx, d2, &j)) return JS_FALSE; j &= 31; d = (op == JSOP_LSH) ? i << j : i >> j; break; case JSOP_URSH: if (!js_DoubleToECMAUint32(cx, d, &u)) return JS_FALSE; if (!js_DoubleToECMAInt32(cx, d2, &j)) return JS_FALSE; j &= 31; d = u >> j; break; case JSOP_ADD: d += d2; break; case JSOP_SUB: d -= d2; break; case JSOP_MUL: d *= d2; break; case JSOP_DIV: if (d2 == 0) { #if defined(XP_WIN) /* XXX MSVC miscompiles such that (NaN == 0) */ if (JSDOUBLE_IS_NaN(d2)) d = *cx->runtime->jsNaN; else #endif if (d == 0 || JSDOUBLE_IS_NaN(d)) d = *cx->runtime->jsNaN; else if ((JSDOUBLE_HI32(d) ^ JSDOUBLE_HI32(d2)) >> 31) d = *cx->runtime->jsNegativeInfinity; else d = *cx->runtime->jsPositiveInfinity; } else { d /= d2; } break; case JSOP_MOD: if (d2 == 0) { d = *cx->runtime->jsNaN; } else { #if defined(XP_WIN) /* Workaround MS fmod bug where 42 % (1/0) => NaN, not 42. */ if (!(JSDOUBLE_IS_FINITE(d) && JSDOUBLE_IS_INFINITE(d2))) #endif d = fmod(d, d2); } break; default:; } /* Take care to allow pn1 or pn2 to alias pn. */ if (pn1 != pn) RecycleTree(pn1, tc); if (pn2 != pn) RecycleTree(pn2, tc); pn->pn_type = TOK_NUMBER; pn->pn_op = JSOP_NUMBER; pn->pn_arity = PN_NULLARY; pn->pn_dval = d; return JS_TRUE; } #if JS_HAS_XML_SUPPORT static JSBool FoldXMLConstants(JSContext *cx, JSParseNode *pn, JSTreeContext *tc) { JSTokenType tt; JSParseNode **pnp, *pn1, *pn2; JSString *accum, *str; uint32 i, j; JS_ASSERT(pn->pn_arity == PN_LIST); tt = pn->pn_type; pnp = &pn->pn_head; pn1 = *pnp; accum = NULL; if ((pn->pn_extra & PNX_CANTFOLD) == 0) { if (tt == TOK_XMLETAGO) accum = ATOM_TO_STRING(cx->runtime->atomState.etagoAtom); else if (tt == TOK_XMLSTAGO || tt == TOK_XMLPTAGC) accum = ATOM_TO_STRING(cx->runtime->atomState.stagoAtom); } for (pn2 = pn1, i = j = 0; pn2; pn2 = pn2->pn_next, i++) { /* The parser already rejected end-tags with attributes. */ JS_ASSERT(tt != TOK_XMLETAGO || i == 0); switch (pn2->pn_type) { case TOK_XMLATTR: if (!accum) goto cantfold; /* FALL THROUGH */ case TOK_XMLNAME: case TOK_XMLSPACE: case TOK_XMLTEXT: case TOK_STRING: if (pn2->pn_arity == PN_LIST) goto cantfold; str = ATOM_TO_STRING(pn2->pn_atom); break; case TOK_XMLCDATA: str = js_MakeXMLCDATAString(cx, ATOM_TO_STRING(pn2->pn_atom)); if (!str) return JS_FALSE; break; case TOK_XMLCOMMENT: str = js_MakeXMLCommentString(cx, ATOM_TO_STRING(pn2->pn_atom)); if (!str) return JS_FALSE; break; case TOK_XMLPI: str = js_MakeXMLPIString(cx, ATOM_TO_STRING(pn2->pn_atom), ATOM_TO_STRING(pn2->pn_atom2)); if (!str) return JS_FALSE; break; cantfold: default: JS_ASSERT(*pnp == pn1); if ((tt == TOK_XMLSTAGO || tt == TOK_XMLPTAGC) && (i & 1) ^ (j & 1)) { #ifdef DEBUG_brendanXXX printf("1: %d, %d => %s\n", i, j, accum ? JS_GetStringBytes(accum) : "NULL"); #endif } else if (accum && pn1 != pn2) { while (pn1->pn_next != pn2) { pn1 = RecycleTree(pn1, tc); --pn->pn_count; } pn1->pn_type = TOK_XMLTEXT; pn1->pn_op = JSOP_STRING; pn1->pn_arity = PN_NULLARY; pn1->pn_atom = js_AtomizeString(cx, accum, 0); if (!pn1->pn_atom) return JS_FALSE; JS_ASSERT(pnp != &pn1->pn_next); *pnp = pn1; } pnp = &pn2->pn_next; pn1 = *pnp; accum = NULL; continue; } if (accum) { str = ((tt == TOK_XMLSTAGO || tt == TOK_XMLPTAGC) && i != 0) ? js_AddAttributePart(cx, i & 1, accum, str) : js_ConcatStrings(cx, accum, str); if (!str) return JS_FALSE; #ifdef DEBUG_brendanXXX printf("2: %d, %d => %s (%u)\n", i, j, JS_GetStringBytes(str), JSSTRING_LENGTH(str)); #endif ++j; } accum = str; } if (accum) { str = NULL; if ((pn->pn_extra & PNX_CANTFOLD) == 0) { if (tt == TOK_XMLPTAGC) str = ATOM_TO_STRING(cx->runtime->atomState.ptagcAtom); else if (tt == TOK_XMLSTAGO || tt == TOK_XMLETAGO) str = ATOM_TO_STRING(cx->runtime->atomState.tagcAtom); } if (str) { accum = js_ConcatStrings(cx, accum, str); if (!accum) return JS_FALSE; } JS_ASSERT(*pnp == pn1); while (pn1->pn_next) { pn1 = RecycleTree(pn1, tc); --pn->pn_count; } pn1->pn_type = TOK_XMLTEXT; pn1->pn_op = JSOP_STRING; pn1->pn_arity = PN_NULLARY; pn1->pn_atom = js_AtomizeString(cx, accum, 0); if (!pn1->pn_atom) return JS_FALSE; JS_ASSERT(pnp != &pn1->pn_next); *pnp = pn1; } if (pn1 && pn->pn_count == 1) { /* * Only one node under pn, and it has been folded: move pn1 onto pn * unless pn is an XML root (in which case we need it to tell the code * generator to emit a JSOP_TOXML or JSOP_TOXMLLIST op). If pn is an * XML root *and* it's a point-tag, rewrite it to TOK_XMLELEM to avoid * extra "<" and "/>" bracketing at runtime. */ if (!(pn->pn_extra & PNX_XMLROOT)) { PN_MOVE_NODE(pn, pn1); } else if (tt == TOK_XMLPTAGC) { pn->pn_type = TOK_XMLELEM; pn->pn_op = JSOP_TOXML; } } return JS_TRUE; } #endif /* JS_HAS_XML_SUPPORT */ static JSBool StartsWith(JSParseNode *pn, JSTokenType tt) { #define TAIL_RECURSE(pn2) JS_BEGIN_MACRO pn = (pn2); goto recur; JS_END_MACRO recur: if (pn->pn_type == tt) return JS_TRUE; switch (pn->pn_arity) { case PN_FUNC: return tt == TOK_FUNCTION; case PN_LIST: if (pn->pn_head) TAIL_RECURSE(pn->pn_head); break; case PN_TERNARY: if (pn->pn_kid1) TAIL_RECURSE(pn->pn_kid1); break; case PN_BINARY: if (pn->pn_left) TAIL_RECURSE(pn->pn_left); break; case PN_UNARY: /* A parenthesized expression starts with a left parenthesis. */ if (pn->pn_type == TOK_RP) return tt == TOK_LP; if (pn->pn_kid) TAIL_RECURSE(pn->pn_kid); break; case PN_NAME: if (pn->pn_type == TOK_DOT || pn->pn_type == TOK_DBLDOT) TAIL_RECURSE(pn->pn_expr); /* FALL THROUGH */ } return JS_FALSE; #undef TAIL_RECURSE } JSBool js_FoldConstants(JSContext *cx, JSParseNode *pn, JSTreeContext *tc) { JSParseNode *pn1 = NULL, *pn2 = NULL, *pn3 = NULL; int stackDummy; if (!JS_CHECK_STACK_SIZE(cx, stackDummy)) { JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_OVER_RECURSED); return JS_FALSE; } switch (pn->pn_arity) { case PN_FUNC: { uint16 oldflags = tc->flags; tc->flags = (uint16) pn->pn_flags; if (!js_FoldConstants(cx, pn->pn_body, tc)) return JS_FALSE; tc->flags = oldflags; break; } case PN_LIST: #if 0 /* JS_HAS_XML_SUPPORT */ switch (pn->pn_type) { case TOK_XMLELEM: case TOK_XMLLIST: case TOK_XMLPTAGC: /* * Try to fold this XML parse tree once, from the top down, into * a JSXML tree with just one object wrapping the tree root. * * Certain subtrees could be folded similarly, but we'd have to * ensure that none used namespace prefixes declared elsewhere in * its super-tree, and we would have to convert each XML object * created at runtime for such sub-trees back into a string, and * concatenate and re-parse anyway. */ if ((pn->pn_extra & (PNX_XMLROOT | PNX_CANTFOLD)) == PNX_XMLROOT && !(tc->flags & TCF_HAS_DEFXMLNS)) { JSObject *obj; JSAtom *atom; obj = js_ParseNodeToXMLObject(cx, pn); if (!obj) return JS_FALSE; atom = js_AtomizeObject(cx, obj, 0); if (!atom) return JS_FALSE; pn->pn_op = JSOP_XMLOBJECT; pn->pn_arity = PN_NULLARY; pn->pn_atom = atom; return JS_TRUE; } /* * Can't fold from parse node to XML tree -- try folding strings * as much as possible, and folding XML sub-trees bottom up to * minimize string concatenation and ToXML/ToXMLList operations * at runtime. */ break; default:; } #endif /* Save the list head in pn1 for later use. */ for (pn1 = pn2 = pn->pn_head; pn2; pn2 = pn2->pn_next) { if (!js_FoldConstants(cx, pn2, tc)) return JS_FALSE; } break; case PN_TERNARY: /* Any kid may be null (e.g. for (;;)). */ pn1 = pn->pn_kid1; pn2 = pn->pn_kid2; pn3 = pn->pn_kid3; if (pn1 && !js_FoldConstants(cx, pn1, tc)) return JS_FALSE; if (pn2 && !js_FoldConstants(cx, pn2, tc)) return JS_FALSE; if (pn3 && !js_FoldConstants(cx, pn3, tc)) return JS_FALSE; break; case PN_BINARY: /* First kid may be null (for default case in switch). */ pn1 = pn->pn_left; pn2 = pn->pn_right; if (pn1 && !js_FoldConstants(cx, pn1, tc)) return JS_FALSE; if (!js_FoldConstants(cx, pn2, tc)) return JS_FALSE; break; case PN_UNARY: /* Our kid may be null (e.g. return; vs. return e;). */ pn1 = pn->pn_kid; if (pn1 && !js_FoldConstants(cx, pn1, tc)) return JS_FALSE; break; case PN_NAME: /* * Skip pn1 down along a chain of dotted member expressions to avoid * excessive recursion. Our only goal here is to fold constants (if * any) in the primary expression operand to the left of the first * dot in the chain. */ pn1 = pn->pn_expr; while (pn1 && pn1->pn_arity == PN_NAME) pn1 = pn1->pn_expr; if (pn1 && !js_FoldConstants(cx, pn1, tc)) return JS_FALSE; break; case PN_NULLARY: break; } switch (pn->pn_type) { case TOK_IF: if (ContainsStmt(pn2, TOK_VAR) || ContainsStmt(pn3, TOK_VAR)) break; /* FALL THROUGH */ case TOK_HOOK: /* Reduce 'if (C) T; else E' into T for true C, E for false. */ while (pn1->pn_type == TOK_RP) pn1 = pn1->pn_kid; switch (pn1->pn_type) { case TOK_NUMBER: if (pn1->pn_dval == 0) pn2 = pn3; break; case TOK_STRING: if (JSSTRING_LENGTH(ATOM_TO_STRING(pn1->pn_atom)) == 0) pn2 = pn3; break; case TOK_PRIMARY: if (pn1->pn_op == JSOP_TRUE) break; if (pn1->pn_op == JSOP_FALSE || pn1->pn_op == JSOP_NULL) { pn2 = pn3; break; } /* FALL THROUGH */ default: /* Early return to dodge common code that copies pn2 to pn. */ return JS_TRUE; } if (pn2) { /* * pn2 is the then- or else-statement subtree to compile. Take * care not to expose an object initialiser, which would be parsed * as a block, to the Statement parser via eval(uneval(e)) where e * is '1 ? {p:2, q:3}[i] : r;' or the like. */ if (pn->pn_type == TOK_HOOK && StartsWith(pn2, TOK_RC)) { pn->pn_type = TOK_RP; pn->pn_arity = PN_UNARY; pn->pn_kid = pn2; } else { PN_MOVE_NODE(pn, pn2); } } if (!pn2 || (pn->pn_type == TOK_SEMI && !pn->pn_kid)) { /* * False condition and no else, or an empty then-statement was * moved up over pn. Either way, make pn an empty block (not an * empty statement, which does not decompile, even when labeled). * NB: pn must be a TOK_IF as TOK_HOOK can never have a null kid * or an empty statement for a child. */ pn->pn_type = TOK_LC; pn->pn_arity = PN_LIST; PN_INIT_LIST(pn); } RecycleTree(pn2, tc); if (pn3 && pn3 != pn2) RecycleTree(pn3, tc); break; case TOK_ASSIGN: /* * Compound operators such as *= should be subject to folding, in case * the left-hand side is constant, and so that the decompiler produces * the same string that you get from decompiling a script or function * compiled from that same string. As with +, += is special. */ if (pn->pn_op == JSOP_NOP) break; if (pn->pn_op != JSOP_ADD) goto do_binary_op; /* FALL THROUGH */ case TOK_PLUS: if (pn->pn_arity == PN_LIST) { size_t length, length2; jschar *chars; JSString *str, *str2; /* * Any string literal term with all others number or string means * this is a concatenation. If any term is not a string or number * literal, we can't fold. */ JS_ASSERT(pn->pn_count > 2); if (pn->pn_extra & PNX_CANTFOLD) return JS_TRUE; if (pn->pn_extra != PNX_STRCAT) goto do_binary_op; /* Ok, we're concatenating: convert non-string constant operands. */ length = 0; for (pn2 = pn1; pn2; pn2 = pn2->pn_next) { if (!FoldType(cx, pn2, TOK_STRING)) return JS_FALSE; /* XXX fold only if all operands convert to string */ if (pn2->pn_type != TOK_STRING) return JS_TRUE; length += ATOM_TO_STRING(pn2->pn_atom)->length; } /* Allocate a new buffer and string descriptor for the result. */ chars = (jschar *) JS_malloc(cx, (length + 1) * sizeof(jschar)); if (!chars) return JS_FALSE; str = js_NewString(cx, chars, length, 0); if (!str) { JS_free(cx, chars); return JS_FALSE; } /* Fill the buffer, advancing chars and recycling kids as we go. */ for (pn2 = pn1; pn2; pn2 = RecycleTree(pn2, tc)) { str2 = ATOM_TO_STRING(pn2->pn_atom); length2 = str2->length; js_strncpy(chars, str2->chars, length2); chars += length2; } *chars = 0; /* Atomize the result string and mutate pn to refer to it. */ pn->pn_atom = js_AtomizeString(cx, str, 0); if (!pn->pn_atom) return JS_FALSE; pn->pn_type = TOK_STRING; pn->pn_op = JSOP_STRING; pn->pn_arity = PN_NULLARY; break; } /* Handle a binary string concatenation. */ JS_ASSERT(pn->pn_arity == PN_BINARY); if (pn1->pn_type == TOK_STRING || pn2->pn_type == TOK_STRING) { JSString *left, *right, *str; if (!FoldType(cx, (pn1->pn_type != TOK_STRING) ? pn1 : pn2, TOK_STRING)) { return JS_FALSE; } if (pn1->pn_type != TOK_STRING || pn2->pn_type != TOK_STRING) return JS_TRUE; left = ATOM_TO_STRING(pn1->pn_atom); right = ATOM_TO_STRING(pn2->pn_atom); str = js_ConcatStrings(cx, left, right); if (!str) return JS_FALSE; pn->pn_atom = js_AtomizeString(cx, str, 0); if (!pn->pn_atom) return JS_FALSE; pn->pn_type = TOK_STRING; pn->pn_op = JSOP_STRING; pn->pn_arity = PN_NULLARY; RecycleTree(pn1, tc); RecycleTree(pn2, tc); break; } /* Can't concatenate string literals, let's try numbers. */ goto do_binary_op; case TOK_STAR: /* The * in 'import *;' parses as a nullary star node. */ if (pn->pn_arity == PN_NULLARY) break; /* FALL THROUGH */ case TOK_SHOP: case TOK_MINUS: case TOK_DIVOP: do_binary_op: if (pn->pn_arity == PN_LIST) { JS_ASSERT(pn->pn_count > 2); for (pn2 = pn1; pn2; pn2 = pn2->pn_next) { if (!FoldType(cx, pn2, TOK_NUMBER)) return JS_FALSE; } for (pn2 = pn1; pn2; pn2 = pn2->pn_next) { /* XXX fold only if all operands convert to number */ if (pn2->pn_type != TOK_NUMBER) break; } if (!pn2) { JSOp op = pn->pn_op; pn2 = pn1->pn_next; pn3 = pn2->pn_next; if (!FoldBinaryNumeric(cx, op, pn1, pn2, pn, tc)) return JS_FALSE; while ((pn2 = pn3) != NULL) { pn3 = pn2->pn_next; if (!FoldBinaryNumeric(cx, op, pn, pn2, pn, tc)) return JS_FALSE; } } } else { JS_ASSERT(pn->pn_arity == PN_BINARY); if (!FoldType(cx, pn1, TOK_NUMBER) || !FoldType(cx, pn2, TOK_NUMBER)) { return JS_FALSE; } if (pn1->pn_type == TOK_NUMBER && pn2->pn_type == TOK_NUMBER) { if (!FoldBinaryNumeric(cx, pn->pn_op, pn1, pn2, pn, tc)) return JS_FALSE; } } break; case TOK_UNARYOP: while (pn1->pn_type == TOK_RP) pn1 = pn1->pn_kid; if (pn1->pn_type == TOK_NUMBER) { jsdouble d; int32 i; /* Operate on one numeric constant. */ d = pn1->pn_dval; switch (pn->pn_op) { case JSOP_BITNOT: if (!js_DoubleToECMAInt32(cx, d, &i)) return JS_FALSE; d = ~i; break; case JSOP_NEG: #ifdef HPUX /* * Negation of a zero doesn't produce a negative * zero on HPUX. Perform the operation by bit * twiddling. */ JSDOUBLE_HI32(d) ^= JSDOUBLE_HI32_SIGNBIT; #else d = -d; #endif break; case JSOP_POS: break; case JSOP_NOT: pn->pn_type = TOK_PRIMARY; pn->pn_op = (d == 0) ? JSOP_TRUE : JSOP_FALSE; pn->pn_arity = PN_NULLARY; /* FALL THROUGH */ default: /* Return early to dodge the common TOK_NUMBER code. */ return JS_TRUE; } pn->pn_type = TOK_NUMBER; pn->pn_op = JSOP_NUMBER; pn->pn_arity = PN_NULLARY; pn->pn_dval = d; RecycleTree(pn1, tc); } break; #if JS_HAS_XML_SUPPORT case TOK_XMLELEM: case TOK_XMLLIST: case TOK_XMLPTAGC: case TOK_XMLSTAGO: case TOK_XMLETAGO: case TOK_XMLNAME: if (pn->pn_arity == PN_LIST) { JS_ASSERT(pn->pn_type == TOK_XMLLIST || pn->pn_count != 0); if (!FoldXMLConstants(cx, pn, tc)) return JS_FALSE; } break; case TOK_AT: if (pn1->pn_type == TOK_XMLNAME) { jsval v; JSAtom *atom; v = ATOM_KEY(pn1->pn_atom); if (!js_ToAttributeName(cx, &v)) return JS_FALSE; JS_ASSERT(!JSVAL_IS_PRIMITIVE(v)); atom = js_AtomizeObject(cx, JSVAL_TO_OBJECT(v), 0); if (!atom) return JS_FALSE; pn->pn_type = TOK_XMLNAME; pn->pn_op = JSOP_OBJECT; pn->pn_arity = PN_NULLARY; pn->pn_atom = atom; RecycleTree(pn1, tc); } break; #endif /* JS_HAS_XML_SUPPORT */ default:; } return JS_TRUE; } pacparser-1.4.5/src/spidermonkey/js/src/jsparse.h000066400000000000000000000541471464010763600220460ustar00rootroot00000000000000/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- * vim: set ts=8 sw=4 et tw=78: * * ***** BEGIN LICENSE BLOCK ***** * Version: MPL 1.1/GPL 2.0/LGPL 2.1 * * The contents of this file are subject to the Mozilla Public License Version * 1.1 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * http://www.mozilla.org/MPL/ * * Software distributed under the License is distributed on an "AS IS" basis, * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License * for the specific language governing rights and limitations under the * License. * * The Original Code is Mozilla Communicator client code, released * March 31, 1998. * * The Initial Developer of the Original Code is * Netscape Communications Corporation. * Portions created by the Initial Developer are Copyright (C) 1998 * the Initial Developer. All Rights Reserved. * * Contributor(s): * * Alternatively, the contents of this file may be used under the terms of * either of the GNU General Public License Version 2 or later (the "GPL"), * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), * in which case the provisions of the GPL or the LGPL are applicable instead * of those above. If you wish to allow use of your version of this file only * under the terms of either the GPL or the LGPL, and not to allow others to * use your version of this file under the terms of the MPL, indicate your * decision by deleting the provisions above and replace them with the notice * and other provisions required by the GPL or the LGPL. If you do not delete * the provisions above, a recipient may use your version of this file under * the terms of any one of the MPL, the GPL or the LGPL. * * ***** END LICENSE BLOCK ***** */ #ifndef jsparse_h___ #define jsparse_h___ /* * JS parser definitions. */ #include "jsconfig.h" #include "jsprvtd.h" #include "jspubtd.h" #include "jsscan.h" JS_BEGIN_EXTERN_C /* * Parsing builds a tree of nodes that directs code generation. This tree is * not a concrete syntax tree in all respects (for example, || and && are left * associative, but (A && B && C) translates into the right-associated tree * > so that code generation can emit a left-associative branch * around when A is false). Nodes are labeled by token type, with a * JSOp secondary label when needed: * * Label Variant Members * ----- ------- ------- * * TOK_FUNCTION func pn_funAtom: atom holding function object containing * arg and var properties. We create the function * object at parse (not emit) time to specialize arg * and var bytecodes early. * pn_body: TOK_LC node for function body statements * pn_flags: TCF_FUN_* flags (see jsemit.h) collected * while parsing the function's body * pn_tryCount: of try statements in function * * * TOK_LC list pn_head: list of pn_count statements * TOK_EXPORT list pn_head: list of pn_count TOK_NAMEs or one TOK_STAR * (which is not a multiply node) * TOK_IMPORT list pn_head: list of pn_count sub-trees of the form * a.b.*, a[b].*, a.*, a.b, or a[b] -- but never a. * Each member is expressed with TOK_DOT or TOK_LB. * Each sub-tree's root node has a pn_op in the set * JSOP_IMPORT{ALL,PROP,ELEM} * TOK_IF ternary pn_kid1: cond, pn_kid2: then, pn_kid3: else or null * TOK_SWITCH binary pn_left: discriminant * pn_right: list of TOK_CASE nodes, with at most one * TOK_DEFAULT node, or if there are let bindings * in the top level of the switch body's cases, a * TOK_LEXICALSCOPE node that contains the list of * TOK_CASE nodes. * TOK_CASE, binary pn_left: case expr or null if TOK_DEFAULT * TOK_DEFAULT pn_right: TOK_LC node for this case's statements * pn_val: constant value if lookup or table switch * TOK_WHILE binary pn_left: cond, pn_right: body * TOK_DO binary pn_left: body, pn_right: cond * TOK_FOR binary pn_left: either * for/in loop: a binary TOK_IN node with * pn_left: TOK_VAR or TOK_NAME to left of 'in' * if TOK_VAR, its pn_extra may have PNX_POPVAR * and PNX_FORINVAR bits set * pn_right: object expr to right of 'in' * for(;;) loop: a ternary TOK_RESERVED node with * pn_kid1: init expr before first ';' * pn_kid2: cond expr before second ';' * pn_kid3: update expr after second ';' * any kid may be null * pn_right: body * TOK_THROW unary pn_op: JSOP_THROW, pn_kid: exception * TOK_TRY ternary pn_kid1: try block * pn_kid2: null or TOK_RESERVED list of * TOK_LEXICALSCOPE nodes, each with pn_expr pointing * to a TOK_CATCH node * pn_kid3: null or finally block * TOK_CATCH ternary pn_kid1: TOK_NAME, TOK_RB, or TOK_RC catch var node * (TOK_RB or TOK_RC if destructuring) * pn_kid2: null or the catch guard expression * pn_kid3: catch block statements * TOK_BREAK name pn_atom: label or null * TOK_CONTINUE name pn_atom: label or null * TOK_WITH binary pn_left: head expr, pn_right: body * TOK_VAR list pn_head: list of pn_count TOK_NAME nodes * each name node has * pn_atom: variable name * pn_expr: initializer or null * TOK_RETURN unary pn_kid: return expr or null * TOK_SEMI unary pn_kid: expr or null statement * TOK_COLON name pn_atom: label, pn_expr: labeled statement * * * All left-associated binary trees of the same type are optimized into lists * to avoid recursion when processing expression chains. * TOK_COMMA list pn_head: list of pn_count comma-separated exprs * TOK_ASSIGN binary pn_left: lvalue, pn_right: rvalue * pn_op: JSOP_ADD for +=, etc. * TOK_HOOK ternary pn_kid1: cond, pn_kid2: then, pn_kid3: else * TOK_OR binary pn_left: first in || chain, pn_right: rest of chain * TOK_AND binary pn_left: first in && chain, pn_right: rest of chain * TOK_BITOR binary pn_left: left-assoc | expr, pn_right: ^ expr * TOK_BITXOR binary pn_left: left-assoc ^ expr, pn_right: & expr * TOK_BITAND binary pn_left: left-assoc & expr, pn_right: EQ expr * TOK_EQOP binary pn_left: left-assoc EQ expr, pn_right: REL expr * pn_op: JSOP_EQ, JSOP_NE, JSOP_NEW_EQ, JSOP_NEW_NE * TOK_RELOP binary pn_left: left-assoc REL expr, pn_right: SH expr * pn_op: JSOP_LT, JSOP_LE, JSOP_GT, JSOP_GE * TOK_SHOP binary pn_left: left-assoc SH expr, pn_right: ADD expr * pn_op: JSOP_LSH, JSOP_RSH, JSOP_URSH * TOK_PLUS, binary pn_left: left-assoc ADD expr, pn_right: MUL expr * pn_extra: if a left-associated binary TOK_PLUS * tree has been flattened into a list (see above * under ), pn_extra will contain * PNX_STRCAT if at least one list element is a * string literal (TOK_STRING); if such a list has * any non-string, non-number term, pn_extra will * contain PNX_CANTFOLD. * pn_ * TOK_MINUS pn_op: JSOP_ADD, JSOP_SUB * TOK_STAR, binary pn_left: left-assoc MUL expr, pn_right: UNARY expr * TOK_DIVOP pn_op: JSOP_MUL, JSOP_DIV, JSOP_MOD * TOK_UNARYOP unary pn_kid: UNARY expr, pn_op: JSOP_NEG, JSOP_POS, * JSOP_NOT, JSOP_BITNOT, JSOP_TYPEOF, JSOP_VOID * TOK_INC, unary pn_kid: MEMBER expr * TOK_DEC * TOK_NEW list pn_head: list of ctor, arg1, arg2, ... argN * pn_count: 1 + N (where N is number of args) * ctor is a MEMBER expr * TOK_DELETE unary pn_kid: MEMBER expr * TOK_DOT, name pn_expr: MEMBER expr to left of . * TOK_DBLDOT pn_atom: name to right of . * TOK_LB binary pn_left: MEMBER expr to left of [ * pn_right: expr between [ and ] * TOK_LP list pn_head: list of call, arg1, arg2, ... argN * pn_count: 1 + N (where N is number of args) * call is a MEMBER expr naming a callable object * TOK_RB list pn_head: list of pn_count array element exprs * [,,] holes are represented by TOK_COMMA nodes * #n=[...] produces TOK_DEFSHARP at head of list * pn_extra: PN_ENDCOMMA if extra comma at end * TOK_RC list pn_head: list of pn_count TOK_COLON nodes where * each has pn_left: property id, pn_right: value * #n={...} produces TOK_DEFSHARP at head of list * TOK_DEFSHARP unary pn_num: jsint value of n in #n= * pn_kid: null for #n=[...] and #n={...}, primary * if #n=primary for function, paren, name, object * literal expressions * TOK_USESHARP nullary pn_num: jsint value of n in #n# * TOK_RP unary pn_kid: parenthesized expression * TOK_NAME, name pn_atom: name, string, or object atom * TOK_STRING, pn_op: JSOP_NAME, JSOP_STRING, or JSOP_OBJECT, or * JSOP_REGEXP * TOK_OBJECT If JSOP_NAME, pn_op may be JSOP_*ARG or JSOP_*VAR * with pn_slot >= 0 and pn_attrs telling const-ness * TOK_NUMBER dval pn_dval: double value of numeric literal * TOK_PRIMARY nullary pn_op: JSOp bytecode * * * TOK_ANYNAME nullary pn_op: JSOP_ANYNAME * pn_atom: cx->runtime->atomState.starAtom * TOK_AT unary pn_op: JSOP_TOATTRNAME; pn_kid attribute id/expr * TOK_DBLCOLON binary pn_op: JSOP_QNAME * pn_left: TOK_ANYNAME or TOK_NAME node * pn_right: TOK_STRING "*" node, or expr within [] * name pn_op: JSOP_QNAMECONST * pn_expr: TOK_ANYNAME or TOK_NAME left operand * pn_atom: name on right of :: * TOK_XMLELEM list XML element node * pn_head: start tag, content1, ... contentN, end tag * pn_count: 2 + N where N is number of content nodes * N may be > x.length() if {expr} embedded * TOK_XMLLIST list XML list node * pn_head: content1, ... contentN * TOK_XMLSTAGO, list XML start, end, and point tag contents * TOK_XMLETAGC, pn_head: tag name or {expr}, ... XML attrs ... * TOK_XMLPTAGO * TOK_XMLNAME nullary pn_atom: XML name, with no {expr} embedded * TOK_XMLNAME list pn_head: tag name or {expr}, ... name or {expr} * TOK_XMLATTR, nullary pn_atom: attribute value string; pn_op: JSOP_STRING * TOK_XMLCDATA, * TOK_XMLCOMMENT * TOK_XMLPI nullary pn_atom: XML processing instruction target * pn_atom2: XML PI content, or null if no content * TOK_XMLTEXT nullary pn_atom: marked-up text, or null if empty string * TOK_LC unary {expr} in XML tag or content; pn_kid is expr * * So an XML tag with no {expr} and three attributes is a list with the form: * * (tagname attrname1 attrvalue1 attrname2 attrvalue2 attrname2 attrvalue3) * * An XML tag with embedded expressions like so: * * * * would have the form: * * ((name1 {expr1}) (name2 {expr2} name3) {expr3}) * * where () bracket a list with elements separated by spaces, and {expr} is a * TOK_LC unary node with expr as its kid. * * Thus, the attribute name/value pairs occupy successive odd and even list * locations, where pn_head is the TOK_XMLNAME node at list location 0. The * parser builds the same sort of structures for elements: * * Hi there!How are you?{x + y} * * translates to: * * ((a x {x}) 'Hi there!' ((b y {y}) 'How are you?') ((answer) {x + y})) * * * * Label Variant Members * ----- ------- ------- * TOK_LEXICALSCOPE name pn_op: JSOP_LEAVEBLOCK or JSOP_LEAVEBLOCKEXPR * pn_atom: block object * pn_expr: block body * TOK_ARRAYCOMP list pn_head: list of pn_count (1 or 2) elements * if pn_count is 2, first element is #n=[...] * last element is block enclosing for loop(s) * and optionally if-guarded TOK_ARRAYPUSH * pn_extra: stack slot, used during code gen * TOK_ARRAYPUSH unary pn_op: JSOP_ARRAYCOMP * pn_kid: array comprehension expression */ typedef enum JSParseNodeArity { PN_FUNC = -3, PN_LIST = -2, PN_TERNARY = 3, PN_BINARY = 2, PN_UNARY = 1, PN_NAME = -1, PN_NULLARY = 0 } JSParseNodeArity; struct JSParseNode { uint16 pn_type; uint8 pn_op; int8 pn_arity; JSTokenPos pn_pos; ptrdiff_t pn_offset; /* first generated bytecode offset */ union { struct { /* TOK_FUNCTION node */ JSAtom *funAtom; /* atomized function object */ JSParseNode *body; /* TOK_LC list of statements */ uint32 flags; /* accumulated tree context flags */ uint32 tryCount; /* count of try statements in body */ } func; struct { /* list of next-linked nodes */ JSParseNode *head; /* first node in list */ JSParseNode **tail; /* ptr to ptr to last node in list */ uint32 count; /* number of nodes in list */ uint32 extra; /* extra flags, see below */ } list; struct { /* ternary: if, for(;;), ?: */ JSParseNode *kid1; /* condition, discriminant, etc. */ JSParseNode *kid2; /* then-part, case list, etc. */ JSParseNode *kid3; /* else-part, default case, etc. */ } ternary; struct { /* two kids if binary */ JSParseNode *left; JSParseNode *right; jsval val; /* switch case value */ } binary; struct { /* one kid if unary */ JSParseNode *kid; jsint num; /* -1 or sharp variable number */ } unary; struct { /* name, labeled statement, etc. */ JSAtom *atom; /* name or label atom, null if slot */ JSParseNode *expr; /* object or initializer */ jsint slot; /* -1 or arg or local var slot */ uintN attrs; /* attributes if local var or const */ } name; struct { JSAtom *atom; /* first atom in pair */ JSAtom *atom2; /* second atom in pair or null */ } apair; jsdouble dval; /* aligned numeric literal value */ } pn_u; JSParseNode *pn_next; /* to align dval and pn_u on RISCs */ JSTokenStream *pn_ts; /* token stream for error reports */ JSAtom *pn_source; /* saved source for decompilation */ }; #define pn_funAtom pn_u.func.funAtom #define pn_body pn_u.func.body #define pn_flags pn_u.func.flags #define pn_tryCount pn_u.func.tryCount #define pn_head pn_u.list.head #define pn_tail pn_u.list.tail #define pn_count pn_u.list.count #define pn_extra pn_u.list.extra #define pn_kid1 pn_u.ternary.kid1 #define pn_kid2 pn_u.ternary.kid2 #define pn_kid3 pn_u.ternary.kid3 #define pn_left pn_u.binary.left #define pn_right pn_u.binary.right #define pn_val pn_u.binary.val #define pn_kid pn_u.unary.kid #define pn_num pn_u.unary.num #define pn_atom pn_u.name.atom #define pn_expr pn_u.name.expr #define pn_slot pn_u.name.slot #define pn_attrs pn_u.name.attrs #define pn_dval pn_u.dval #define pn_atom2 pn_u.apair.atom2 /* PN_LIST pn_extra flags. */ #define PNX_STRCAT 0x01 /* TOK_PLUS list has string term */ #define PNX_CANTFOLD 0x02 /* TOK_PLUS list has unfoldable term */ #define PNX_POPVAR 0x04 /* TOK_VAR last result needs popping */ #define PNX_FORINVAR 0x08 /* TOK_VAR is left kid of TOK_IN node, which is left kid of TOK_FOR */ #define PNX_ENDCOMMA 0x10 /* array literal has comma at end */ #define PNX_XMLROOT 0x20 /* top-most node in XML literal tree */ #define PNX_GROUPINIT 0x40 /* var [a, b] = [c, d]; unit list */ #define PNX_NEEDBRACES 0x80 /* braces necessary due to closure */ /* * Move pn2 into pn, preserving pn->pn_pos and pn->pn_offset and handing off * any kids in pn2->pn_u, by clearing pn2. */ #define PN_MOVE_NODE(pn, pn2) \ JS_BEGIN_MACRO \ (pn)->pn_type = (pn2)->pn_type; \ (pn)->pn_op = (pn2)->pn_op; \ (pn)->pn_arity = (pn2)->pn_arity; \ (pn)->pn_u = (pn2)->pn_u; \ PN_CLEAR_NODE(pn2); \ JS_END_MACRO #define PN_CLEAR_NODE(pn) \ JS_BEGIN_MACRO \ (pn)->pn_type = TOK_EOF; \ (pn)->pn_op = JSOP_NOP; \ (pn)->pn_arity = PN_NULLARY; \ JS_END_MACRO /* True if pn is a parsenode representing a literal constant. */ #define PN_IS_CONSTANT(pn) \ ((pn)->pn_type == TOK_NUMBER || \ (pn)->pn_type == TOK_STRING || \ ((pn)->pn_type == TOK_PRIMARY && (pn)->pn_op != JSOP_THIS)) /* * Compute a pointer to the last JSParseNode element in a singly-linked list. * NB: list must be non-empty for correct PN_LAST usage! */ #define PN_LAST(list) \ ((JSParseNode *)((char *)(list)->pn_tail - offsetof(JSParseNode, pn_next))) #define PN_INIT_LIST(list) \ JS_BEGIN_MACRO \ (list)->pn_head = NULL; \ (list)->pn_tail = &(list)->pn_head; \ (list)->pn_count = (list)->pn_extra = 0; \ JS_END_MACRO #define PN_INIT_LIST_1(list, pn) \ JS_BEGIN_MACRO \ (list)->pn_head = (pn); \ (list)->pn_tail = &(pn)->pn_next; \ (list)->pn_count = 1; \ (list)->pn_extra = 0; \ JS_END_MACRO #define PN_APPEND(list, pn) \ JS_BEGIN_MACRO \ *(list)->pn_tail = (pn); \ (list)->pn_tail = &(pn)->pn_next; \ (list)->pn_count++; \ JS_END_MACRO /* * Parse a top-level JS script. * * The caller must prevent the GC from running while this function is active, * because atoms and function newborns are not rooted yet. */ extern JS_FRIEND_API(JSParseNode *) js_ParseTokenStream(JSContext *cx, JSObject *chain, JSTokenStream *ts); extern JS_FRIEND_API(JSBool) js_CompileTokenStream(JSContext *cx, JSObject *chain, JSTokenStream *ts, JSCodeGenerator *cg); extern JSBool js_CompileFunctionBody(JSContext *cx, JSTokenStream *ts, JSFunction *fun); extern JSBool js_FoldConstants(JSContext *cx, JSParseNode *pn, JSTreeContext *tc); #if JS_HAS_XML_SUPPORT JS_FRIEND_API(JSParseNode *) js_ParseXMLTokenStream(JSContext *cx, JSObject *chain, JSTokenStream *ts, JSBool allowList); #endif JS_END_EXTERN_C #endif /* jsparse_h___ */ pacparser-1.4.5/src/spidermonkey/js/src/jsprf.c000066400000000000000000000765611464010763600215220ustar00rootroot00000000000000/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* ***** BEGIN LICENSE BLOCK ***** * Version: MPL 1.1/GPL 2.0/LGPL 2.1 * * The contents of this file are subject to the Mozilla Public License Version * 1.1 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * http://www.mozilla.org/MPL/ * * Software distributed under the License is distributed on an "AS IS" basis, * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License * for the specific language governing rights and limitations under the * License. * * The Original Code is Mozilla Communicator client code, released * March 31, 1998. * * The Initial Developer of the Original Code is * Netscape Communications Corporation. * Portions created by the Initial Developer are Copyright (C) 1998 * the Initial Developer. All Rights Reserved. * * Contributor(s): * * Alternatively, the contents of this file may be used under the terms of * either of the GNU General Public License Version 2 or later (the "GPL"), * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), * in which case the provisions of the GPL or the LGPL are applicable instead * of those above. If you wish to allow use of your version of this file only * under the terms of either the GPL or the LGPL, and not to allow others to * use your version of this file under the terms of the MPL, indicate your * decision by deleting the provisions above and replace them with the notice * and other provisions required by the GPL or the LGPL. If you do not delete * the provisions above, a recipient may use your version of this file under * the terms of any one of the MPL, the GPL or the LGPL. * * ***** END LICENSE BLOCK ***** */ /* ** Portable safe sprintf code. ** ** Author: Kipp E.B. Hickman */ #include "jsstddef.h" #include #include #include #include #include "jsprf.h" #include "jslong.h" #include "jsutil.h" /* Added by JSIFY */ #include "jspubtd.h" #include "jsstr.h" /* ** Note: on some platforms va_list is defined as an array, ** and requires array notation. */ #ifdef HAVE_VA_COPY #define VARARGS_ASSIGN(foo, bar) VA_COPY(foo,bar) #elif defined(HAVE_VA_LIST_AS_ARRAY) #define VARARGS_ASSIGN(foo, bar) foo[0] = bar[0] #else #define VARARGS_ASSIGN(foo, bar) (foo) = (bar) #endif /* ** WARNING: This code may *NOT* call JS_LOG (because JS_LOG calls it) */ /* ** XXX This needs to be internationalized! */ typedef struct SprintfStateStr SprintfState; struct SprintfStateStr { int (*stuff)(SprintfState *ss, const char *sp, JSUint32 len); char *base; char *cur; JSUint32 maxlen; int (*func)(void *arg, const char *sp, JSUint32 len); void *arg; }; /* ** Numbered Arguement State */ struct NumArgState{ int type; /* type of the current ap */ va_list ap; /* point to the corresponding position on ap */ }; #define NAS_DEFAULT_NUM 20 /* default number of NumberedArgumentState array */ #define TYPE_INT16 0 #define TYPE_UINT16 1 #define TYPE_INTN 2 #define TYPE_UINTN 3 #define TYPE_INT32 4 #define TYPE_UINT32 5 #define TYPE_INT64 6 #define TYPE_UINT64 7 #define TYPE_STRING 8 #define TYPE_DOUBLE 9 #define TYPE_INTSTR 10 #define TYPE_WSTRING 11 #define TYPE_UNKNOWN 20 #define FLAG_LEFT 0x1 #define FLAG_SIGNED 0x2 #define FLAG_SPACED 0x4 #define FLAG_ZEROS 0x8 #define FLAG_NEG 0x10 /* ** Fill into the buffer using the data in src */ static int fill2(SprintfState *ss, const char *src, int srclen, int width, int flags) { char space = ' '; int rv; width -= srclen; if ((width > 0) && ((flags & FLAG_LEFT) == 0)) { /* Right adjusting */ if (flags & FLAG_ZEROS) { space = '0'; } while (--width >= 0) { rv = (*ss->stuff)(ss, &space, 1); if (rv < 0) { return rv; } } } /* Copy out the source data */ rv = (*ss->stuff)(ss, src, (JSUint32)srclen); if (rv < 0) { return rv; } if ((width > 0) && ((flags & FLAG_LEFT) != 0)) { /* Left adjusting */ while (--width >= 0) { rv = (*ss->stuff)(ss, &space, 1); if (rv < 0) { return rv; } } } return 0; } /* ** Fill a number. The order is: optional-sign zero-filling conversion-digits */ static int fill_n(SprintfState *ss, const char *src, int srclen, int width, int prec, int type, int flags) { int zerowidth = 0; int precwidth = 0; int signwidth = 0; int leftspaces = 0; int rightspaces = 0; int cvtwidth; int rv; char sign; if ((type & 1) == 0) { if (flags & FLAG_NEG) { sign = '-'; signwidth = 1; } else if (flags & FLAG_SIGNED) { sign = '+'; signwidth = 1; } else if (flags & FLAG_SPACED) { sign = ' '; signwidth = 1; } } cvtwidth = signwidth + srclen; if (prec > 0) { if (prec > srclen) { precwidth = prec - srclen; /* Need zero filling */ cvtwidth += precwidth; } } if ((flags & FLAG_ZEROS) && (prec < 0)) { if (width > cvtwidth) { zerowidth = width - cvtwidth; /* Zero filling */ cvtwidth += zerowidth; } } if (flags & FLAG_LEFT) { if (width > cvtwidth) { /* Space filling on the right (i.e. left adjusting) */ rightspaces = width - cvtwidth; } } else { if (width > cvtwidth) { /* Space filling on the left (i.e. right adjusting) */ leftspaces = width - cvtwidth; } } while (--leftspaces >= 0) { rv = (*ss->stuff)(ss, " ", 1); if (rv < 0) { return rv; } } if (signwidth) { rv = (*ss->stuff)(ss, &sign, 1); if (rv < 0) { return rv; } } while (--precwidth >= 0) { rv = (*ss->stuff)(ss, "0", 1); if (rv < 0) { return rv; } } while (--zerowidth >= 0) { rv = (*ss->stuff)(ss, "0", 1); if (rv < 0) { return rv; } } rv = (*ss->stuff)(ss, src, (JSUint32)srclen); if (rv < 0) { return rv; } while (--rightspaces >= 0) { rv = (*ss->stuff)(ss, " ", 1); if (rv < 0) { return rv; } } return 0; } /* ** Convert a long into its printable form */ static int cvt_l(SprintfState *ss, long num, int width, int prec, int radix, int type, int flags, const char *hexp) { char cvtbuf[100]; char *cvt; int digits; /* according to the man page this needs to happen */ if ((prec == 0) && (num == 0)) { return 0; } /* ** Converting decimal is a little tricky. In the unsigned case we ** need to stop when we hit 10 digits. In the signed case, we can ** stop when the number is zero. */ cvt = cvtbuf + sizeof(cvtbuf); digits = 0; while (num) { int digit = (((unsigned long)num) % radix) & 0xF; *--cvt = hexp[digit]; digits++; num = (long)(((unsigned long)num) / radix); } if (digits == 0) { *--cvt = '0'; digits++; } /* ** Now that we have the number converted without its sign, deal with ** the sign and zero padding. */ return fill_n(ss, cvt, digits, width, prec, type, flags); } /* ** Convert a 64-bit integer into its printable form */ static int cvt_ll(SprintfState *ss, JSInt64 num, int width, int prec, int radix, int type, int flags, const char *hexp) { char cvtbuf[100]; char *cvt; int digits; JSInt64 rad; /* according to the man page this needs to happen */ if ((prec == 0) && (JSLL_IS_ZERO(num))) { return 0; } /* ** Converting decimal is a little tricky. In the unsigned case we ** need to stop when we hit 10 digits. In the signed case, we can ** stop when the number is zero. */ JSLL_I2L(rad, radix); cvt = cvtbuf + sizeof(cvtbuf); digits = 0; while (!JSLL_IS_ZERO(num)) { JSInt32 digit; JSInt64 quot, rem; JSLL_UDIVMOD(", &rem, num, rad); JSLL_L2I(digit, rem); *--cvt = hexp[digit & 0xf]; digits++; num = quot; } if (digits == 0) { *--cvt = '0'; digits++; } /* ** Now that we have the number converted without its sign, deal with ** the sign and zero padding. */ return fill_n(ss, cvt, digits, width, prec, type, flags); } /* ** Convert a double precision floating point number into its printable ** form. ** ** XXX stop using sprintf to convert floating point */ static int cvt_f(SprintfState *ss, double d, const char *fmt0, const char *fmt1) { char fin[20]; char fout[300]; int amount = fmt1 - fmt0; JS_ASSERT((amount > 0) && (amount < (int)sizeof(fin))); if (amount >= (int)sizeof(fin)) { /* Totally bogus % command to sprintf. Just ignore it */ return 0; } memcpy(fin, fmt0, (size_t)amount); fin[amount] = 0; /* Convert floating point using the native sprintf code */ #ifdef DEBUG { const char *p = fin; while (*p) { JS_ASSERT(*p != 'L'); p++; } } #endif sprintf(fout, fin, d); /* ** This assert will catch overflow's of fout, when building with ** debugging on. At least this way we can track down the evil piece ** of calling code and fix it! */ JS_ASSERT(strlen(fout) < sizeof(fout)); return (*ss->stuff)(ss, fout, strlen(fout)); } /* ** Convert a string into its printable form. "width" is the output ** width. "prec" is the maximum number of characters of "s" to output, ** where -1 means until NUL. */ static int cvt_s(SprintfState *ss, const char *s, int width, int prec, int flags) { int slen; if (prec == 0) return 0; /* Limit string length by precision value */ slen = s ? strlen(s) : 6; if (prec > 0) { if (prec < slen) { slen = prec; } } /* and away we go */ return fill2(ss, s ? s : "(null)", slen, width, flags); } static int cvt_ws(SprintfState *ss, const jschar *ws, int width, int prec, int flags) { int result; /* * Supply NULL as the JSContext; errors are not reported, * and malloc() is used to allocate the buffer buffer. */ if (ws) { int slen = js_strlen(ws); char *s = js_DeflateString(NULL, ws, slen); if (!s) return -1; /* JSStuffFunc error indicator. */ result = cvt_s(ss, s, width, prec, flags); free(s); } else { result = cvt_s(ss, NULL, width, prec, flags); } return result; } /* ** BuildArgArray stands for Numbered Argument list Sprintf ** for example, ** fmp = "%4$i, %2$d, %3s, %1d"; ** the number must start from 1, and no gap among them */ static struct NumArgState* BuildArgArray( const char *fmt, va_list ap, int* rv, struct NumArgState* nasArray ) { int number = 0, cn = 0, i; const char *p; char c; struct NumArgState *nas; /* ** first pass: ** detemine how many legal % I have got, then allocate space */ p = fmt; *rv = 0; i = 0; while( ( c = *p++ ) != 0 ){ if( c != '%' ) continue; if( ( c = *p++ ) == '%' ) /* skip %% case */ continue; while( c != 0 ){ if( c > '9' || c < '0' ){ if( c == '$' ){ /* numbered argument csae */ if( i > 0 ){ *rv = -1; return NULL; } number++; } else { /* non-numbered argument case */ if( number > 0 ){ *rv = -1; return NULL; } i = 1; } break; } c = *p++; } } if( number == 0 ){ return NULL; } if( number > NAS_DEFAULT_NUM ){ nas = (struct NumArgState*)malloc( number * sizeof( struct NumArgState ) ); if( !nas ){ *rv = -1; return NULL; } } else { nas = nasArray; } for( i = 0; i < number; i++ ){ nas[i].type = TYPE_UNKNOWN; } /* ** second pass: ** set nas[].type */ p = fmt; while( ( c = *p++ ) != 0 ){ if( c != '%' ) continue; c = *p++; if( c == '%' ) continue; cn = 0; while( c && c != '$' ){ /* should improve error check later */ cn = cn*10 + c - '0'; c = *p++; } if( !c || cn < 1 || cn > number ){ *rv = -1; break; } /* nas[cn] starts from 0, and make sure nas[cn].type is not assigned */ cn--; if( nas[cn].type != TYPE_UNKNOWN ) continue; c = *p++; /* width */ if (c == '*') { /* not supported feature, for the argument is not numbered */ *rv = -1; break; } while ((c >= '0') && (c <= '9')) { c = *p++; } /* precision */ if (c == '.') { c = *p++; if (c == '*') { /* not supported feature, for the argument is not numbered */ *rv = -1; break; } while ((c >= '0') && (c <= '9')) { c = *p++; } } /* size */ nas[cn].type = TYPE_INTN; if (c == 'h') { nas[cn].type = TYPE_INT16; c = *p++; } else if (c == 'L') { /* XXX not quite sure here */ nas[cn].type = TYPE_INT64; c = *p++; } else if (c == 'l') { nas[cn].type = TYPE_INT32; c = *p++; if (c == 'l') { nas[cn].type = TYPE_INT64; c = *p++; } } /* format */ switch (c) { case 'd': case 'c': case 'i': case 'o': case 'u': case 'x': case 'X': break; case 'e': case 'f': case 'g': nas[ cn ].type = TYPE_DOUBLE; break; case 'p': /* XXX should use cpp */ if (sizeof(void *) == sizeof(JSInt32)) { nas[ cn ].type = TYPE_UINT32; } else if (sizeof(void *) == sizeof(JSInt64)) { nas[ cn ].type = TYPE_UINT64; } else if (sizeof(void *) == sizeof(JSIntn)) { nas[ cn ].type = TYPE_UINTN; } else { nas[ cn ].type = TYPE_UNKNOWN; } break; case 'C': case 'S': case 'E': case 'G': /* XXX not supported I suppose */ JS_ASSERT(0); nas[ cn ].type = TYPE_UNKNOWN; break; case 's': nas[ cn ].type = (nas[ cn ].type == TYPE_UINT16) ? TYPE_WSTRING : TYPE_STRING; break; case 'n': nas[ cn ].type = TYPE_INTSTR; break; default: JS_ASSERT(0); nas[ cn ].type = TYPE_UNKNOWN; break; } /* get a legal para. */ if( nas[ cn ].type == TYPE_UNKNOWN ){ *rv = -1; break; } } /* ** third pass ** fill the nas[cn].ap */ if( *rv < 0 ){ if( nas != nasArray ) free( nas ); return NULL; } cn = 0; while( cn < number ){ if( nas[cn].type == TYPE_UNKNOWN ){ cn++; continue; } VARARGS_ASSIGN(nas[cn].ap, ap); switch( nas[cn].type ){ case TYPE_INT16: case TYPE_UINT16: case TYPE_INTN: case TYPE_UINTN: (void)va_arg( ap, JSIntn ); break; case TYPE_INT32: (void)va_arg( ap, JSInt32 ); break; case TYPE_UINT32: (void)va_arg( ap, JSUint32 ); break; case TYPE_INT64: (void)va_arg( ap, JSInt64 ); break; case TYPE_UINT64: (void)va_arg( ap, JSUint64 ); break; case TYPE_STRING: (void)va_arg( ap, char* ); break; case TYPE_WSTRING: (void)va_arg( ap, jschar* ); break; case TYPE_INTSTR: (void)va_arg( ap, JSIntn* ); break; case TYPE_DOUBLE: (void)va_arg( ap, double ); break; default: if( nas != nasArray ) free( nas ); *rv = -1; return NULL; } cn++; } return nas; } /* ** The workhorse sprintf code. */ static int dosprintf(SprintfState *ss, const char *fmt, va_list ap) { char c; int flags, width, prec, radix, type; union { char ch; jschar wch; int i; long l; JSInt64 ll; double d; const char *s; const jschar* ws; int *ip; } u; const char *fmt0; static char *hex = "0123456789abcdef"; static char *HEX = "0123456789ABCDEF"; char *hexp; int rv, i; struct NumArgState *nas = NULL; struct NumArgState nasArray[ NAS_DEFAULT_NUM ]; char pattern[20]; const char *dolPt = NULL; /* in "%4$.2f", dolPt will poiont to . */ #ifdef JS_C_STRINGS_ARE_UTF8 char utf8buf[6]; int utf8len; #endif /* ** build an argument array, IF the fmt is numbered argument ** list style, to contain the Numbered Argument list pointers */ nas = BuildArgArray( fmt, ap, &rv, nasArray ); if( rv < 0 ){ /* the fmt contains error Numbered Argument format, jliu@netscape.com */ JS_ASSERT(0); return rv; } while ((c = *fmt++) != 0) { if (c != '%') { rv = (*ss->stuff)(ss, fmt - 1, 1); if (rv < 0) { return rv; } continue; } fmt0 = fmt - 1; /* ** Gobble up the % format string. Hopefully we have handled all ** of the strange cases! */ flags = 0; c = *fmt++; if (c == '%') { /* quoting a % with %% */ rv = (*ss->stuff)(ss, fmt - 1, 1); if (rv < 0) { return rv; } continue; } if( nas != NULL ){ /* the fmt contains the Numbered Arguments feature */ i = 0; while( c && c != '$' ){ /* should imporve error check later */ i = ( i * 10 ) + ( c - '0' ); c = *fmt++; } if( nas[i-1].type == TYPE_UNKNOWN ){ if( nas && ( nas != nasArray ) ) free( nas ); return -1; } ap = nas[i-1].ap; dolPt = fmt; c = *fmt++; } /* * Examine optional flags. Note that we do not implement the * '#' flag of sprintf(). The ANSI C spec. of the '#' flag is * somewhat ambiguous and not ideal, which is perhaps why * the various sprintf() implementations are inconsistent * on this feature. */ while ((c == '-') || (c == '+') || (c == ' ') || (c == '0')) { if (c == '-') flags |= FLAG_LEFT; if (c == '+') flags |= FLAG_SIGNED; if (c == ' ') flags |= FLAG_SPACED; if (c == '0') flags |= FLAG_ZEROS; c = *fmt++; } if (flags & FLAG_SIGNED) flags &= ~FLAG_SPACED; if (flags & FLAG_LEFT) flags &= ~FLAG_ZEROS; /* width */ if (c == '*') { c = *fmt++; width = va_arg(ap, int); } else { width = 0; while ((c >= '0') && (c <= '9')) { width = (width * 10) + (c - '0'); c = *fmt++; } } /* precision */ prec = -1; if (c == '.') { c = *fmt++; if (c == '*') { c = *fmt++; prec = va_arg(ap, int); } else { prec = 0; while ((c >= '0') && (c <= '9')) { prec = (prec * 10) + (c - '0'); c = *fmt++; } } } /* size */ type = TYPE_INTN; if (c == 'h') { type = TYPE_INT16; c = *fmt++; } else if (c == 'L') { /* XXX not quite sure here */ type = TYPE_INT64; c = *fmt++; } else if (c == 'l') { type = TYPE_INT32; c = *fmt++; if (c == 'l') { type = TYPE_INT64; c = *fmt++; } } /* format */ hexp = hex; switch (c) { case 'd': case 'i': /* decimal/integer */ radix = 10; goto fetch_and_convert; case 'o': /* octal */ radix = 8; type |= 1; goto fetch_and_convert; case 'u': /* unsigned decimal */ radix = 10; type |= 1; goto fetch_and_convert; case 'x': /* unsigned hex */ radix = 16; type |= 1; goto fetch_and_convert; case 'X': /* unsigned HEX */ radix = 16; hexp = HEX; type |= 1; goto fetch_and_convert; fetch_and_convert: switch (type) { case TYPE_INT16: u.l = va_arg(ap, int); if (u.l < 0) { u.l = -u.l; flags |= FLAG_NEG; } goto do_long; case TYPE_UINT16: u.l = va_arg(ap, int) & 0xffff; goto do_long; case TYPE_INTN: u.l = va_arg(ap, int); if (u.l < 0) { u.l = -u.l; flags |= FLAG_NEG; } goto do_long; case TYPE_UINTN: u.l = (long)va_arg(ap, unsigned int); goto do_long; case TYPE_INT32: u.l = va_arg(ap, JSInt32); if (u.l < 0) { u.l = -u.l; flags |= FLAG_NEG; } goto do_long; case TYPE_UINT32: u.l = (long)va_arg(ap, JSUint32); do_long: rv = cvt_l(ss, u.l, width, prec, radix, type, flags, hexp); if (rv < 0) { return rv; } break; case TYPE_INT64: u.ll = va_arg(ap, JSInt64); if (!JSLL_GE_ZERO(u.ll)) { JSLL_NEG(u.ll, u.ll); flags |= FLAG_NEG; } goto do_longlong; case TYPE_UINT64: u.ll = va_arg(ap, JSUint64); do_longlong: rv = cvt_ll(ss, u.ll, width, prec, radix, type, flags, hexp); if (rv < 0) { return rv; } break; } break; case 'e': case 'E': case 'f': case 'g': u.d = va_arg(ap, double); if( nas != NULL ){ i = fmt - dolPt; if( i < (int)sizeof( pattern ) ){ pattern[0] = '%'; memcpy( &pattern[1], dolPt, (size_t)i ); rv = cvt_f(ss, u.d, pattern, &pattern[i+1] ); } } else rv = cvt_f(ss, u.d, fmt0, fmt); if (rv < 0) { return rv; } break; case 'c': if ((flags & FLAG_LEFT) == 0) { while (width-- > 1) { rv = (*ss->stuff)(ss, " ", 1); if (rv < 0) { return rv; } } } switch (type) { case TYPE_INT16: /* Treat %hc as %c if JS_C_STRINGS_ARE_UTF8 is undefined. */ #ifdef JS_C_STRINGS_ARE_UTF8 u.wch = va_arg(ap, int); utf8len = js_OneUcs4ToUtf8Char (utf8buf, u.wch); rv = (*ss->stuff)(ss, utf8buf, utf8len); break; #endif case TYPE_INTN: u.ch = va_arg(ap, int); rv = (*ss->stuff)(ss, &u.ch, 1); break; } if (rv < 0) { return rv; } if (flags & FLAG_LEFT) { while (width-- > 1) { rv = (*ss->stuff)(ss, " ", 1); if (rv < 0) { return rv; } } } break; case 'p': if (sizeof(void *) == sizeof(JSInt32)) { type = TYPE_UINT32; } else if (sizeof(void *) == sizeof(JSInt64)) { type = TYPE_UINT64; } else if (sizeof(void *) == sizeof(int)) { type = TYPE_UINTN; } else { JS_ASSERT(0); break; } radix = 16; goto fetch_and_convert; #if 0 case 'C': case 'S': case 'E': case 'G': /* XXX not supported I suppose */ JS_ASSERT(0); break; #endif case 's': if(type == TYPE_INT16) { /* * This would do a simple string/byte conversion * if JS_C_STRINGS_ARE_UTF8 is not defined. */ u.ws = va_arg(ap, const jschar*); rv = cvt_ws(ss, u.ws, width, prec, flags); } else { u.s = va_arg(ap, const char*); rv = cvt_s(ss, u.s, width, prec, flags); } if (rv < 0) { return rv; } break; case 'n': u.ip = va_arg(ap, int*); if (u.ip) { *u.ip = ss->cur - ss->base; } break; default: /* Not a % token after all... skip it */ #if 0 JS_ASSERT(0); #endif rv = (*ss->stuff)(ss, "%", 1); if (rv < 0) { return rv; } rv = (*ss->stuff)(ss, fmt - 1, 1); if (rv < 0) { return rv; } } } /* Stuff trailing NUL */ rv = (*ss->stuff)(ss, "\0", 1); if( nas && ( nas != nasArray ) ){ free( nas ); } return rv; } /************************************************************************/ static int FuncStuff(SprintfState *ss, const char *sp, JSUint32 len) { int rv; rv = (*ss->func)(ss->arg, sp, len); if (rv < 0) { return rv; } ss->maxlen += len; return 0; } JS_PUBLIC_API(JSUint32) JS_sxprintf(JSStuffFunc func, void *arg, const char *fmt, ...) { va_list ap; int rv; va_start(ap, fmt); rv = JS_vsxprintf(func, arg, fmt, ap); va_end(ap); return rv; } JS_PUBLIC_API(JSUint32) JS_vsxprintf(JSStuffFunc func, void *arg, const char *fmt, va_list ap) { SprintfState ss; int rv; ss.stuff = FuncStuff; ss.func = func; ss.arg = arg; ss.maxlen = 0; rv = dosprintf(&ss, fmt, ap); return (rv < 0) ? (JSUint32)-1 : ss.maxlen; } /* ** Stuff routine that automatically grows the malloc'd output buffer ** before it overflows. */ static int GrowStuff(SprintfState *ss, const char *sp, JSUint32 len) { ptrdiff_t off; char *newbase; JSUint32 newlen; off = ss->cur - ss->base; if (off + len >= ss->maxlen) { /* Grow the buffer */ newlen = ss->maxlen + ((len > 32) ? len : 32); if (ss->base) { newbase = (char*) realloc(ss->base, newlen); } else { newbase = (char*) malloc(newlen); } if (!newbase) { /* Ran out of memory */ return -1; } ss->base = newbase; ss->maxlen = newlen; ss->cur = ss->base + off; } /* Copy data */ while (len) { --len; *ss->cur++ = *sp++; } JS_ASSERT((JSUint32)(ss->cur - ss->base) <= ss->maxlen); return 0; } /* ** sprintf into a malloc'd buffer */ JS_PUBLIC_API(char *) JS_smprintf(const char *fmt, ...) { va_list ap; char *rv; va_start(ap, fmt); rv = JS_vsmprintf(fmt, ap); va_end(ap); return rv; } /* ** Free memory allocated, for the caller, by JS_smprintf */ JS_PUBLIC_API(void) JS_smprintf_free(char *mem) { free(mem); } JS_PUBLIC_API(char *) JS_vsmprintf(const char *fmt, va_list ap) { SprintfState ss; int rv; ss.stuff = GrowStuff; ss.base = 0; ss.cur = 0; ss.maxlen = 0; rv = dosprintf(&ss, fmt, ap); if (rv < 0) { if (ss.base) { free(ss.base); } return 0; } return ss.base; } /* ** Stuff routine that discards overflow data */ static int LimitStuff(SprintfState *ss, const char *sp, JSUint32 len) { JSUint32 limit = ss->maxlen - (ss->cur - ss->base); if (len > limit) { len = limit; } while (len) { --len; *ss->cur++ = *sp++; } return 0; } /* ** sprintf into a fixed size buffer. Make sure there is a NUL at the end ** when finished. */ JS_PUBLIC_API(JSUint32) JS_snprintf(char *out, JSUint32 outlen, const char *fmt, ...) { va_list ap; int rv; JS_ASSERT((JSInt32)outlen > 0); if ((JSInt32)outlen <= 0) { return 0; } va_start(ap, fmt); rv = JS_vsnprintf(out, outlen, fmt, ap); va_end(ap); return rv; } JS_PUBLIC_API(JSUint32) JS_vsnprintf(char *out, JSUint32 outlen,const char *fmt, va_list ap) { SprintfState ss; JSUint32 n; JS_ASSERT((JSInt32)outlen > 0); if ((JSInt32)outlen <= 0) { return 0; } ss.stuff = LimitStuff; ss.base = out; ss.cur = out; ss.maxlen = outlen; (void) dosprintf(&ss, fmt, ap); /* If we added chars, and we didn't append a null, do it now. */ if( (ss.cur != ss.base) && (ss.cur[-1] != '\0') ) ss.cur[-1] = '\0'; n = ss.cur - ss.base; return n ? n - 1 : n; } JS_PUBLIC_API(char *) JS_sprintf_append(char *last, const char *fmt, ...) { va_list ap; char *rv; va_start(ap, fmt); rv = JS_vsprintf_append(last, fmt, ap); va_end(ap); return rv; } JS_PUBLIC_API(char *) JS_vsprintf_append(char *last, const char *fmt, va_list ap) { SprintfState ss; int rv; ss.stuff = GrowStuff; if (last) { int lastlen = strlen(last); ss.base = last; ss.cur = last + lastlen; ss.maxlen = lastlen; } else { ss.base = 0; ss.cur = 0; ss.maxlen = 0; } rv = dosprintf(&ss, fmt, ap); if (rv < 0) { if (ss.base) { free(ss.base); } return 0; } return ss.base; } pacparser-1.4.5/src/spidermonkey/js/src/jsprf.h000066400000000000000000000140461464010763600215150ustar00rootroot00000000000000/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* ***** BEGIN LICENSE BLOCK ***** * Version: MPL 1.1/GPL 2.0/LGPL 2.1 * * The contents of this file are subject to the Mozilla Public License Version * 1.1 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * http://www.mozilla.org/MPL/ * * Software distributed under the License is distributed on an "AS IS" basis, * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License * for the specific language governing rights and limitations under the * License. * * The Original Code is Mozilla Communicator client code, released * March 31, 1998. * * The Initial Developer of the Original Code is * Netscape Communications Corporation. * Portions created by the Initial Developer are Copyright (C) 1998 * the Initial Developer. All Rights Reserved. * * Contributor(s): * * Alternatively, the contents of this file may be used under the terms of * either of the GNU General Public License Version 2 or later (the "GPL"), * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), * in which case the provisions of the GPL or the LGPL are applicable instead * of those above. If you wish to allow use of your version of this file only * under the terms of either the GPL or the LGPL, and not to allow others to * use your version of this file under the terms of the MPL, indicate your * decision by deleting the provisions above and replace them with the notice * and other provisions required by the GPL or the LGPL. If you do not delete * the provisions above, a recipient may use your version of this file under * the terms of any one of the MPL, the GPL or the LGPL. * * ***** END LICENSE BLOCK ***** */ #ifndef jsprf_h___ #define jsprf_h___ /* ** API for PR printf like routines. Supports the following formats ** %d - decimal ** %u - unsigned decimal ** %x - unsigned hex ** %X - unsigned uppercase hex ** %o - unsigned octal ** %hd, %hu, %hx, %hX, %ho - 16-bit versions of above ** %ld, %lu, %lx, %lX, %lo - 32-bit versions of above ** %lld, %llu, %llx, %llX, %llo - 64 bit versions of above ** %s - string ** %hs - 16-bit version of above (only available if compiled with JS_C_STRINGS_ARE_UTF8) ** %c - character ** %hc - 16-bit version of above (only available if compiled with JS_C_STRINGS_ARE_UTF8) ** %p - pointer (deals with machine dependent pointer size) ** %f - float ** %g - float */ #include "jstypes.h" #include #include JS_BEGIN_EXTERN_C /* ** sprintf into a fixed size buffer. Guarantees that a NUL is at the end ** of the buffer. Returns the length of the written output, NOT including ** the NUL, or (JSUint32)-1 if an error occurs. */ extern JS_PUBLIC_API(JSUint32) JS_snprintf(char *out, JSUint32 outlen, const char *fmt, ...); /* ** sprintf into a malloc'd buffer. Return a pointer to the malloc'd ** buffer on success, NULL on failure. Call "JS_smprintf_free" to release ** the memory returned. */ extern JS_PUBLIC_API(char*) JS_smprintf(const char *fmt, ...); /* ** Free the memory allocated, for the caller, by JS_smprintf */ extern JS_PUBLIC_API(void) JS_smprintf_free(char *mem); /* ** "append" sprintf into a malloc'd buffer. "last" is the last value of ** the malloc'd buffer. sprintf will append data to the end of last, ** growing it as necessary using realloc. If last is NULL, JS_sprintf_append ** will allocate the initial string. The return value is the new value of ** last for subsequent calls, or NULL if there is a malloc failure. */ extern JS_PUBLIC_API(char*) JS_sprintf_append(char *last, const char *fmt, ...); /* ** sprintf into a function. The function "f" is called with a string to ** place into the output. "arg" is an opaque pointer used by the stuff ** function to hold any state needed to do the storage of the output ** data. The return value is a count of the number of characters fed to ** the stuff function, or (JSUint32)-1 if an error occurs. */ typedef JSIntn (*JSStuffFunc)(void *arg, const char *s, JSUint32 slen); extern JS_PUBLIC_API(JSUint32) JS_sxprintf(JSStuffFunc f, void *arg, const char *fmt, ...); /* ** va_list forms of the above. */ extern JS_PUBLIC_API(JSUint32) JS_vsnprintf(char *out, JSUint32 outlen, const char *fmt, va_list ap); extern JS_PUBLIC_API(char*) JS_vsmprintf(const char *fmt, va_list ap); extern JS_PUBLIC_API(char*) JS_vsprintf_append(char *last, const char *fmt, va_list ap); extern JS_PUBLIC_API(JSUint32) JS_vsxprintf(JSStuffFunc f, void *arg, const char *fmt, va_list ap); /* *************************************************************************** ** FUNCTION: JS_sscanf ** DESCRIPTION: ** JS_sscanf() scans the input character string, performs data ** conversions, and stores the converted values in the data objects ** pointed to by its arguments according to the format control ** string. ** ** JS_sscanf() behaves the same way as the sscanf() function in the ** Standard C Library (stdio.h), with the following exceptions: ** - JS_sscanf() handles the NSPR integer and floating point types, ** such as JSInt16, JSInt32, JSInt64, and JSFloat64, whereas ** sscanf() handles the standard C types like short, int, long, ** and double. ** - JS_sscanf() has no multibyte character support, while sscanf() ** does. ** INPUTS: ** const char *buf ** a character string holding the input to scan ** const char *fmt ** the format control string for the conversions ** ... ** variable number of arguments, each of them is a pointer to ** a data object in which the converted value will be stored ** OUTPUTS: none ** RETURNS: JSInt32 ** The number of values converted and stored. ** RESTRICTIONS: ** Multibyte characters in 'buf' or 'fmt' are not allowed. *************************************************************************** */ extern JS_PUBLIC_API(JSInt32) JS_sscanf(const char *buf, const char *fmt, ...); JS_END_EXTERN_C #endif /* jsprf_h___ */ pacparser-1.4.5/src/spidermonkey/js/src/jsproto.tbl000066400000000000000000000115751464010763600224270ustar00rootroot00000000000000/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- * vim: set sw=4 ts=8 et tw=80 ft=c: * * ***** BEGIN LICENSE BLOCK ***** * Version: MPL 1.1/GPL 2.0/LGPL 2.1 * * The contents of this file are subject to the Mozilla Public License Version * 1.1 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * http://www.mozilla.org/MPL/ * * Software distributed under the License is distributed on an "AS IS" basis, * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License * for the specific language governing rights and limitations under the * License. * * The Original Code is SpiderMonkey 1.7 work in progress, released * February 14, 2006. * * The Initial Developer of the Original Code is * Brendan Eich * * Contributor(s): * * Alternatively, the contents of this file may be used under the terms of * either of the GNU General Public License Version 2 or later (the "GPL"), * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), * in which case the provisions of the GPL or the LGPL are applicable instead * of those above. If you wish to allow use of your version of this file only * under the terms of either the GPL or the LGPL, and not to allow others to * use your version of this file under the terms of the MPL, indicate your * decision by deleting the provisions above and replace them with the notice * and other provisions required by the GPL or the LGPL. If you do not delete * the provisions above, a recipient may use your version of this file under * the terms of any one of the MPL, the GPL or the LGPL. * * ***** END LICENSE BLOCK ***** */ #include "jsconfig.h" #if JS_HAS_SCRIPT_OBJECT # define SCRIPT_INIT js_InitScriptClass #else # define SCRIPT_INIT js_InitNullClass #endif #if JS_HAS_XML_SUPPORT # define XML_INIT js_InitXMLClass # define NAMESPACE_INIT js_InitNamespaceClass # define QNAME_INIT js_InitQNameClass # define ANYNAME_INIT js_InitAnyNameClass # define ATTRIBUTE_INIT js_InitAttributeNameClass #else # define XML_INIT js_InitNullClass # define NAMESPACE_INIT js_InitNullClass # define QNAME_INIT js_InitNullClass # define ANYNAME_INIT js_InitNullClass # define ATTRIBUTE_INIT js_InitNullClass #endif #if JS_HAS_GENERATORS # define GENERATOR_INIT js_InitIteratorClasses #else # define GENERATOR_INIT js_InitNullClass #endif #if JS_HAS_FILE_OBJECT # define FILE_INIT js_InitFileClass #else # define FILE_INIT js_InitNullClass #endif /* * Enumerator codes in the second column must not change -- they are part of * the JS XDR API. */ JS_PROTO(Null, 0, js_InitNullClass) JS_PROTO(Object, 1, js_InitFunctionAndObjectClasses) JS_PROTO(Function, 2, js_InitFunctionAndObjectClasses) JS_PROTO(Array, 3, js_InitArrayClass) JS_PROTO(Boolean, 4, js_InitBooleanClass) JS_PROTO(Call, 5, js_InitCallClass) JS_PROTO(Date, 6, js_InitDateClass) JS_PROTO(Math, 7, js_InitMathClass) JS_PROTO(Number, 8, js_InitNumberClass) JS_PROTO(String, 9, js_InitStringClass) JS_PROTO(RegExp, 10, js_InitRegExpClass) JS_PROTO(Script, 11, SCRIPT_INIT) JS_PROTO(XML, 12, XML_INIT) JS_PROTO(Namespace, 13, NAMESPACE_INIT) JS_PROTO(QName, 14, QNAME_INIT) JS_PROTO(AnyName, 15, ANYNAME_INIT) JS_PROTO(AttributeName, 16, ATTRIBUTE_INIT) JS_PROTO(Error, 17, js_InitExceptionClasses) JS_PROTO(InternalError, 18, js_InitExceptionClasses) JS_PROTO(EvalError, 19, js_InitExceptionClasses) JS_PROTO(RangeError, 20, js_InitExceptionClasses) JS_PROTO(ReferenceError, 21, js_InitExceptionClasses) JS_PROTO(SyntaxError, 22, js_InitExceptionClasses) JS_PROTO(TypeError, 23, js_InitExceptionClasses) JS_PROTO(URIError, 24, js_InitExceptionClasses) JS_PROTO(Generator, 25, GENERATOR_INIT) JS_PROTO(Iterator, 26, js_InitIteratorClasses) JS_PROTO(StopIteration, 27, js_InitIteratorClasses) JS_PROTO(UnusedProto28, 28, js_InitNullClass) JS_PROTO(File, 29, FILE_INIT) JS_PROTO(Block, 30, js_InitBlockClass) #undef SCRIPT_INIT #undef XML_INIT #undef NAMESPACE_INIT #undef QNAME_INIT #undef ANYNAME_INIT #undef ATTRIBUTE_INIT #undef GENERATOR_INIT #undef FILE_INIT pacparser-1.4.5/src/spidermonkey/js/src/jsprvtd.h000066400000000000000000000213251464010763600220630ustar00rootroot00000000000000/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- * * ***** BEGIN LICENSE BLOCK ***** * Version: MPL 1.1/GPL 2.0/LGPL 2.1 * * The contents of this file are subject to the Mozilla Public License Version * 1.1 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * http://www.mozilla.org/MPL/ * * Software distributed under the License is distributed on an "AS IS" basis, * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License * for the specific language governing rights and limitations under the * License. * * The Original Code is Mozilla Communicator client code, released * March 31, 1998. * * The Initial Developer of the Original Code is * Netscape Communications Corporation. * Portions created by the Initial Developer are Copyright (C) 1998 * the Initial Developer. All Rights Reserved. * * Contributor(s): * * Alternatively, the contents of this file may be used under the terms of * either of the GNU General Public License Version 2 or later (the "GPL"), * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), * in which case the provisions of the GPL or the LGPL are applicable instead * of those above. If you wish to allow use of your version of this file only * under the terms of either the GPL or the LGPL, and not to allow others to * use your version of this file under the terms of the MPL, indicate your * decision by deleting the provisions above and replace them with the notice * and other provisions required by the GPL or the LGPL. If you do not delete * the provisions above, a recipient may use your version of this file under * the terms of any one of the MPL, the GPL or the LGPL. * * ***** END LICENSE BLOCK ***** */ #ifndef jsprvtd_h___ #define jsprvtd_h___ /* * JS private typename definitions. * * This header is included only in other .h files, for convenience and for * simplicity of type naming. The alternative for structures is to use tags, * which are named the same as their typedef names (legal in C/C++, and less * noisy than suffixing the typedef name with "Struct" or "Str"). Instead, * all .h files that include this file may use the same typedef name, whether * declaring a pointer to struct type, or defining a member of struct type. * * A few fundamental scalar types are defined here too. Neither the scalar * nor the struct typedefs should change much, therefore the nearly-global * make dependency induced by this file should not prove painful. */ #include "jspubtd.h" /* Internal identifier (jsid) macros. */ #define JSID_ATOM 0x0 #define JSID_INT 0x1 #define JSID_OBJECT 0x2 #define JSID_TAGMASK 0x3 #define JSID_TAG(id) ((id) & JSID_TAGMASK) #define JSID_SETTAG(id,t) ((id) | (t)) #define JSID_CLRTAG(id) ((id) & ~(jsid)JSID_TAGMASK) #define JSID_IS_ATOM(id) (JSID_TAG(id) == JSID_ATOM) #define JSID_TO_ATOM(id) ((JSAtom *)(id)) #define ATOM_TO_JSID(atom) ((jsid)(atom)) #define ATOM_JSID_TO_JSVAL(id) ATOM_KEY(JSID_TO_ATOM(id)) #define JSID_IS_INT(id) ((id) & JSID_INT) #define JSID_TO_INT(id) ((jsint)(id) >> 1) #define INT_TO_JSID(i) (((jsint)(i) << 1) | JSID_INT) #define INT_JSID_TO_JSVAL(id) (id) #define INT_JSVAL_TO_JSID(v) (v) #define JSID_IS_OBJECT(id) (JSID_TAG(id) == JSID_OBJECT) #define JSID_TO_OBJECT(id) ((JSObject *) JSID_CLRTAG(id)) #define OBJECT_TO_JSID(obj) ((jsid)(obj) | JSID_OBJECT) #define OBJECT_JSID_TO_JSVAL(id) OBJECT_TO_JSVAL(JSID_CLRTAG(id)) #define OBJECT_JSVAL_TO_JSID(v) OBJECT_TO_JSID(JSVAL_TO_OBJECT(v)) /* Scalar typedefs. */ typedef uint8 jsbytecode; typedef uint8 jssrcnote; typedef uint32 jsatomid; /* Struct typedefs. */ typedef struct JSArgumentFormatMap JSArgumentFormatMap; typedef struct JSCodeGenerator JSCodeGenerator; typedef struct JSDependentString JSDependentString; typedef struct JSGCThing JSGCThing; typedef struct JSGenerator JSGenerator; typedef struct JSParseNode JSParseNode; typedef struct JSSharpObjectMap JSSharpObjectMap; typedef struct JSThread JSThread; typedef struct JSToken JSToken; typedef struct JSTokenPos JSTokenPos; typedef struct JSTokenPtr JSTokenPtr; typedef struct JSTokenStream JSTokenStream; typedef struct JSTreeContext JSTreeContext; typedef struct JSTryNote JSTryNote; /* Friend "Advanced API" typedefs. */ typedef struct JSAtom JSAtom; typedef struct JSAtomList JSAtomList; typedef struct JSAtomListElement JSAtomListElement; typedef struct JSAtomMap JSAtomMap; typedef struct JSAtomState JSAtomState; typedef struct JSCodeSpec JSCodeSpec; typedef struct JSPrinter JSPrinter; typedef struct JSRegExp JSRegExp; typedef struct JSRegExpStatics JSRegExpStatics; typedef struct JSScope JSScope; typedef struct JSScopeOps JSScopeOps; typedef struct JSScopeProperty JSScopeProperty; typedef struct JSStackHeader JSStackHeader; typedef struct JSStringBuffer JSStringBuffer; typedef struct JSSubString JSSubString; typedef struct JSXML JSXML; typedef struct JSXMLNamespace JSXMLNamespace; typedef struct JSXMLQName JSXMLQName; typedef struct JSXMLArray JSXMLArray; typedef struct JSXMLArrayCursor JSXMLArrayCursor; /* "Friend" types used by jscntxt.h and jsdbgapi.h. */ typedef enum JSTrapStatus { JSTRAP_ERROR, JSTRAP_CONTINUE, JSTRAP_RETURN, JSTRAP_THROW, JSTRAP_LIMIT } JSTrapStatus; typedef JSTrapStatus (* JS_DLL_CALLBACK JSTrapHandler)(JSContext *cx, JSScript *script, jsbytecode *pc, jsval *rval, void *closure); typedef JSBool (* JS_DLL_CALLBACK JSWatchPointHandler)(JSContext *cx, JSObject *obj, jsval id, jsval old, jsval *newp, void *closure); /* called just after script creation */ typedef void (* JS_DLL_CALLBACK JSNewScriptHook)(JSContext *cx, const char *filename, /* URL of script */ uintN lineno, /* first line */ JSScript *script, JSFunction *fun, void *callerdata); /* called just before script destruction */ typedef void (* JS_DLL_CALLBACK JSDestroyScriptHook)(JSContext *cx, JSScript *script, void *callerdata); typedef void (* JS_DLL_CALLBACK JSSourceHandler)(const char *filename, uintN lineno, jschar *str, size_t length, void **listenerTSData, void *closure); /* * This hook captures high level script execution and function calls (JS or * native). It is used by JS_SetExecuteHook to hook top level scripts and by * JS_SetCallHook to hook function calls. It will get called twice per script * or function call: just before execution begins and just after it finishes. * In both cases the 'current' frame is that of the executing code. * * The 'before' param is JS_TRUE for the hook invocation before the execution * and JS_FALSE for the invocation after the code has run. * * The 'ok' param is significant only on the post execution invocation to * signify whether or not the code completed 'normally'. * * The 'closure' param is as passed to JS_SetExecuteHook or JS_SetCallHook * for the 'before'invocation, but is whatever value is returned from that * invocation for the 'after' invocation. Thus, the hook implementor *could* * allocate a structure in the 'before' invocation and return a pointer to that * structure. The pointer would then be handed to the hook for the 'after' * invocation. Alternately, the 'before' could just return the same value as * in 'closure' to cause the 'after' invocation to be called with the same * 'closure' value as the 'before'. * * Returning NULL in the 'before' hook will cause the 'after' hook *not* to * be called. */ typedef void * (* JS_DLL_CALLBACK JSInterpreterHook)(JSContext *cx, JSStackFrame *fp, JSBool before, JSBool *ok, void *closure); typedef void (* JS_DLL_CALLBACK JSObjectHook)(JSContext *cx, JSObject *obj, JSBool isNew, void *closure); typedef JSBool (* JS_DLL_CALLBACK JSDebugErrorHook)(JSContext *cx, const char *message, JSErrorReport *report, void *closure); #endif /* jsprvtd_h___ */ pacparser-1.4.5/src/spidermonkey/js/src/jspubtd.h000066400000000000000000000653511464010763600220510ustar00rootroot00000000000000/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- * * ***** BEGIN LICENSE BLOCK ***** * Version: MPL 1.1/GPL 2.0/LGPL 2.1 * * The contents of this file are subject to the Mozilla Public License Version * 1.1 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * http://www.mozilla.org/MPL/ * * Software distributed under the License is distributed on an "AS IS" basis, * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License * for the specific language governing rights and limitations under the * License. * * The Original Code is Mozilla Communicator client code, released * March 31, 1998. * * The Initial Developer of the Original Code is * Netscape Communications Corporation. * Portions created by the Initial Developer are Copyright (C) 1998 * the Initial Developer. All Rights Reserved. * * Contributor(s): * * Alternatively, the contents of this file may be used under the terms of * either of the GNU General Public License Version 2 or later (the "GPL"), * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), * in which case the provisions of the GPL or the LGPL are applicable instead * of those above. If you wish to allow use of your version of this file only * under the terms of either the GPL or the LGPL, and not to allow others to * use your version of this file under the terms of the MPL, indicate your * decision by deleting the provisions above and replace them with the notice * and other provisions required by the GPL or the LGPL. If you do not delete * the provisions above, a recipient may use your version of this file under * the terms of any one of the MPL, the GPL or the LGPL. * * ***** END LICENSE BLOCK ***** */ #ifndef jspubtd_h___ #define jspubtd_h___ /* * JS public API typedefs. */ #include "jstypes.h" #include "jscompat.h" JS_BEGIN_EXTERN_C /* Scalar typedefs. */ typedef uint16 jschar; typedef int32 jsint; typedef uint32 jsuint; typedef float64 jsdouble; typedef jsword jsval; typedef jsword jsid; typedef int32 jsrefcount; /* PRInt32 if JS_THREADSAFE, see jslock.h */ /* * Run-time version enumeration. See jsconfig.h for compile-time counterparts * to these values that may be selected by the JS_VERSION macro, and tested by * #if expressions. */ typedef enum JSVersion { JSVERSION_1_0 = 100, JSVERSION_1_1 = 110, JSVERSION_1_2 = 120, JSVERSION_1_3 = 130, JSVERSION_1_4 = 140, JSVERSION_ECMA_3 = 148, JSVERSION_1_5 = 150, JSVERSION_1_6 = 160, JSVERSION_1_7 = 170, JSVERSION_DEFAULT = 0, JSVERSION_UNKNOWN = -1 } JSVersion; #define JSVERSION_IS_ECMA(version) \ ((version) == JSVERSION_DEFAULT || (version) >= JSVERSION_1_3) /* Result of typeof operator enumeration. */ typedef enum JSType { JSTYPE_VOID, /* undefined */ JSTYPE_OBJECT, /* object */ JSTYPE_FUNCTION, /* function */ JSTYPE_STRING, /* string */ JSTYPE_NUMBER, /* number */ JSTYPE_BOOLEAN, /* boolean */ JSTYPE_NULL, /* null */ JSTYPE_XML, /* xml object */ JSTYPE_LIMIT } JSType; /* Dense index into cached prototypes and class atoms for standard objects. */ typedef enum JSProtoKey { #define JS_PROTO(name,code,init) JSProto_##name = code, #include "jsproto.tbl" #undef JS_PROTO JSProto_LIMIT } JSProtoKey; /* JSObjectOps.checkAccess mode enumeration. */ typedef enum JSAccessMode { JSACC_PROTO = 0, /* XXXbe redundant w.r.t. id */ JSACC_PARENT = 1, /* XXXbe redundant w.r.t. id */ JSACC_IMPORT = 2, /* import foo.bar */ JSACC_WATCH = 3, /* a watchpoint on object foo for id 'bar' */ JSACC_READ = 4, /* a "get" of foo.bar */ JSACC_WRITE = 8, /* a "set" of foo.bar = baz */ JSACC_LIMIT } JSAccessMode; #define JSACC_TYPEMASK (JSACC_WRITE - 1) /* * This enum type is used to control the behavior of a JSObject property * iterator function that has type JSNewEnumerate. */ typedef enum JSIterateOp { JSENUMERATE_INIT, /* Create new iterator state */ JSENUMERATE_NEXT, /* Iterate once */ JSENUMERATE_DESTROY /* Destroy iterator state */ } JSIterateOp; /* Struct typedefs. */ typedef struct JSClass JSClass; typedef struct JSExtendedClass JSExtendedClass; typedef struct JSConstDoubleSpec JSConstDoubleSpec; typedef struct JSContext JSContext; typedef struct JSErrorReport JSErrorReport; typedef struct JSFunction JSFunction; typedef struct JSFunctionSpec JSFunctionSpec; typedef struct JSIdArray JSIdArray; typedef struct JSProperty JSProperty; typedef struct JSPropertySpec JSPropertySpec; typedef struct JSObject JSObject; typedef struct JSObjectMap JSObjectMap; typedef struct JSObjectOps JSObjectOps; typedef struct JSXMLObjectOps JSXMLObjectOps; typedef struct JSRuntime JSRuntime; typedef struct JSRuntime JSTaskState; /* XXX deprecated name */ typedef struct JSScript JSScript; typedef struct JSStackFrame JSStackFrame; typedef struct JSString JSString; typedef struct JSXDRState JSXDRState; typedef struct JSExceptionState JSExceptionState; typedef struct JSLocaleCallbacks JSLocaleCallbacks; /* JSClass (and JSObjectOps where appropriate) function pointer typedefs. */ /* * Add, delete, get or set a property named by id in obj. Note the jsval id * type -- id may be a string (Unicode property identifier) or an int (element * index). The *vp out parameter, on success, is the new property value after * an add, get, or set. After a successful delete, *vp is JSVAL_FALSE iff * obj[id] can't be deleted (because it's permanent). */ typedef JSBool (* JS_DLL_CALLBACK JSPropertyOp)(JSContext *cx, JSObject *obj, jsval id, jsval *vp); /* * This function type is used for callbacks that enumerate the properties of * a JSObject. The behavior depends on the value of enum_op: * * JSENUMERATE_INIT * A new, opaque iterator state should be allocated and stored in *statep. * (You can use PRIVATE_TO_JSVAL() to tag the pointer to be stored). * * The number of properties that will be enumerated should be returned as * an integer jsval in *idp, if idp is non-null, and provided the number of * enumerable properties is known. If idp is non-null and the number of * enumerable properties can't be computed in advance, *idp should be set * to JSVAL_ZERO. * * JSENUMERATE_NEXT * A previously allocated opaque iterator state is passed in via statep. * Return the next jsid in the iteration using *idp. The opaque iterator * state pointed at by statep is destroyed and *statep is set to JSVAL_NULL * if there are no properties left to enumerate. * * JSENUMERATE_DESTROY * Destroy the opaque iterator state previously allocated in *statep by a * call to this function when enum_op was JSENUMERATE_INIT. * * The return value is used to indicate success, with a value of JS_FALSE * indicating failure. */ typedef JSBool (* JS_DLL_CALLBACK JSNewEnumerateOp)(JSContext *cx, JSObject *obj, JSIterateOp enum_op, jsval *statep, jsid *idp); /* * The old-style JSClass.enumerate op should define all lazy properties not * yet reflected in obj. */ typedef JSBool (* JS_DLL_CALLBACK JSEnumerateOp)(JSContext *cx, JSObject *obj); /* * Resolve a lazy property named by id in obj by defining it directly in obj. * Lazy properties are those reflected from some peer native property space * (e.g., the DOM attributes for a given node reflected as obj) on demand. * * JS looks for a property in an object, and if not found, tries to resolve * the given id. If resolve succeeds, the engine looks again in case resolve * defined obj[id]. If no such property exists directly in obj, the process * is repeated with obj's prototype, etc. * * NB: JSNewResolveOp provides a cheaper way to resolve lazy properties. */ typedef JSBool (* JS_DLL_CALLBACK JSResolveOp)(JSContext *cx, JSObject *obj, jsval id); /* * Like JSResolveOp, but flags provide contextual information as follows: * * JSRESOLVE_QUALIFIED a qualified property id: obj.id or obj[id], not id * JSRESOLVE_ASSIGNING obj[id] is on the left-hand side of an assignment * JSRESOLVE_DETECTING 'if (o.p)...' or similar detection opcode sequence * JSRESOLVE_DECLARING var, const, or function prolog declaration opcode * JSRESOLVE_CLASSNAME class name used when constructing * * The *objp out parameter, on success, should be null to indicate that id * was not resolved; and non-null, referring to obj or one of its prototypes, * if id was resolved. * * This hook instead of JSResolveOp is called via the JSClass.resolve member * if JSCLASS_NEW_RESOLVE is set in JSClass.flags. * * Setting JSCLASS_NEW_RESOLVE and JSCLASS_NEW_RESOLVE_GETS_START further * extends this hook by passing in the starting object on the prototype chain * via *objp. Thus a resolve hook implementation may define the property id * being resolved in the object in which the id was first sought, rather than * in a prototype object whose class led to the resolve hook being called. * * When using JSCLASS_NEW_RESOLVE_GETS_START, the resolve hook must therefore * null *objp to signify "not resolved". With only JSCLASS_NEW_RESOLVE and no * JSCLASS_NEW_RESOLVE_GETS_START, the hook can assume *objp is null on entry. * This is not good practice, but enough existing hook implementations count * on it that we can't break compatibility by passing the starting object in * *objp without a new JSClass flag. */ typedef JSBool (* JS_DLL_CALLBACK JSNewResolveOp)(JSContext *cx, JSObject *obj, jsval id, uintN flags, JSObject **objp); /* * Convert obj to the given type, returning true with the resulting value in * *vp on success, and returning false on error or exception. */ typedef JSBool (* JS_DLL_CALLBACK JSConvertOp)(JSContext *cx, JSObject *obj, JSType type, jsval *vp); /* * Finalize obj, which the garbage collector has determined to be unreachable * from other live objects or from GC roots. Obviously, finalizers must never * store a reference to obj. */ typedef void (* JS_DLL_CALLBACK JSFinalizeOp)(JSContext *cx, JSObject *obj); /* * Used by JS_AddExternalStringFinalizer and JS_RemoveExternalStringFinalizer * to extend and reduce the set of string types finalized by the GC. */ typedef void (* JS_DLL_CALLBACK JSStringFinalizeOp)(JSContext *cx, JSString *str); /* * The signature for JSClass.getObjectOps, used by JS_NewObject's internals * to discover the set of high-level object operations to use for new objects * of the given class. All native objects have a JSClass, which is stored as * a private (int-tagged) pointer in obj->slots[JSSLOT_CLASS]. In contrast, * all native and host objects have a JSObjectMap at obj->map, which may be * shared among a number of objects, and which contains the JSObjectOps *ops * pointer used to dispatch object operations from API calls. * * Thus JSClass (which pre-dates JSObjectOps in the API) provides a low-level * interface to class-specific code and data, while JSObjectOps allows for a * higher level of operation, which does not use the object's class except to * find the class's JSObjectOps struct, by calling clasp->getObjectOps, and to * finalize the object. * * If this seems backwards, that's because it is! API compatibility requires * a JSClass *clasp parameter to JS_NewObject, etc. Most host objects do not * need to implement the larger JSObjectOps, and can share the common JSScope * code and data used by the native (js_ObjectOps, see jsobj.c) ops. * * Further extension to preserve API compatibility: if this function returns * a pointer to JSXMLObjectOps.base, not to JSObjectOps, then the engine calls * extended hooks needed for E4X. */ typedef JSObjectOps * (* JS_DLL_CALLBACK JSGetObjectOps)(JSContext *cx, JSClass *clasp); /* * JSClass.checkAccess type: check whether obj[id] may be accessed per mode, * returning false on error/exception, true on success with obj[id]'s last-got * value in *vp, and its attributes in *attrsp. As for JSPropertyOp above, id * is either a string or an int jsval. * * See JSCheckAccessIdOp, below, for the JSObjectOps counterpart, which takes * a jsid (a tagged int or aligned, unique identifier pointer) rather than a * jsval. The native js_ObjectOps.checkAccess simply forwards to the object's * clasp->checkAccess, so that both JSClass and JSObjectOps implementors may * specialize access checks. */ typedef JSBool (* JS_DLL_CALLBACK JSCheckAccessOp)(JSContext *cx, JSObject *obj, jsval id, JSAccessMode mode, jsval *vp); /* * Encode or decode an object, given an XDR state record representing external * data. See jsxdrapi.h. */ typedef JSBool (* JS_DLL_CALLBACK JSXDRObjectOp)(JSXDRState *xdr, JSObject **objp); /* * Check whether v is an instance of obj. Return false on error or exception, * true on success with JS_TRUE in *bp if v is an instance of obj, JS_FALSE in * *bp otherwise. */ typedef JSBool (* JS_DLL_CALLBACK JSHasInstanceOp)(JSContext *cx, JSObject *obj, jsval v, JSBool *bp); /* * Function type for JSClass.mark and JSObjectOps.mark, called from the GC to * scan live GC-things reachable from obj's private data structure. For each * such thing, a mark implementation must call * * JS_MarkGCThing(cx, thing, name, arg); * * The trailing name and arg parameters are used for GC_MARK_DEBUG-mode heap * dumping and ref-path tracing. The mark function should pass a (typically * literal) string naming the private data member for name, and it must pass * the opaque arg parameter through from its caller. * * For the JSObjectOps.mark hook, the return value is the number of slots at * obj->slots to scan. For JSClass.mark, the return value is ignored. * * NB: JSMarkOp implementations cannot allocate new GC-things (JS_NewObject * called from a mark function will fail silently, e.g.). */ typedef uint32 (* JS_DLL_CALLBACK JSMarkOp)(JSContext *cx, JSObject *obj, void *arg); /* * The optional JSClass.reserveSlots hook allows a class to make computed * per-instance object slots reservations, in addition to or instead of using * JSCLASS_HAS_RESERVED_SLOTS(n) in the JSClass.flags initializer to reserve * a constant-per-class number of slots. Implementations of this hook should * return the number of slots to reserve, not including any reserved by using * JSCLASS_HAS_RESERVED_SLOTS(n) in JSClass.flags. * * NB: called with obj locked by the JSObjectOps-specific mutual exclusion * mechanism appropriate for obj, so don't nest other operations that might * also lock obj. */ typedef uint32 (* JS_DLL_CALLBACK JSReserveSlotsOp)(JSContext *cx, JSObject *obj); /* JSObjectOps function pointer typedefs. */ /* * Create a new subclass of JSObjectMap (see jsobj.h), with the nrefs and ops * members initialized from the same-named parameters, and with the nslots and * freeslot members initialized according to ops and clasp. Return null on * error, non-null on success. * * JSObjectMaps are reference-counted by generic code in the engine. Usually, * the nrefs parameter to JSObjectOps.newObjectMap will be 1, to count the ref * returned to the caller on success. After a successful construction, some * number of js_HoldObjectMap and js_DropObjectMap calls ensue. When nrefs * reaches 0 due to a js_DropObjectMap call, JSObjectOps.destroyObjectMap will * be called to dispose of the map. */ typedef JSObjectMap * (* JS_DLL_CALLBACK JSNewObjectMapOp)(JSContext *cx, jsrefcount nrefs, JSObjectOps *ops, JSClass *clasp, JSObject *obj); /* * Generic type for an infallible JSObjectMap operation, used currently by * JSObjectOps.destroyObjectMap. */ typedef void (* JS_DLL_CALLBACK JSObjectMapOp)(JSContext *cx, JSObjectMap *map); /* * Look for id in obj and its prototype chain, returning false on error or * exception, true on success. On success, return null in *propp if id was * not found. If id was found, return the first object searching from obj * along its prototype chain in which id names a direct property in *objp, and * return a non-null, opaque property pointer in *propp. * * If JSLookupPropOp succeeds and returns with *propp non-null, that pointer * may be passed as the prop parameter to a JSAttributesOp, as a short-cut * that bypasses id re-lookup. In any case, a non-null *propp result after a * successful lookup must be dropped via JSObjectOps.dropProperty. * * NB: successful return with non-null *propp means the implementation may * have locked *objp and added a reference count associated with *propp, so * callers should not risk deadlock by nesting or interleaving other lookups * or any obj-bearing ops before dropping *propp. */ typedef JSBool (* JS_DLL_CALLBACK JSLookupPropOp)(JSContext *cx, JSObject *obj, jsid id, JSObject **objp, JSProperty **propp); /* * Define obj[id], a direct property of obj named id, having the given initial * value, with the specified getter, setter, and attributes. If the propp out * param is non-null, *propp on successful return contains an opaque property * pointer usable as a speedup hint with JSAttributesOp. But note that propp * may be null, indicating that the caller is not interested in recovering an * opaque pointer to the newly-defined property. * * If propp is non-null and JSDefinePropOp succeeds, its caller must be sure * to drop *propp using JSObjectOps.dropProperty in short order, just as with * JSLookupPropOp. */ typedef JSBool (* JS_DLL_CALLBACK JSDefinePropOp)(JSContext *cx, JSObject *obj, jsid id, jsval value, JSPropertyOp getter, JSPropertyOp setter, uintN attrs, JSProperty **propp); /* * Get, set, or delete obj[id], returning false on error or exception, true * on success. If getting or setting, the new value is returned in *vp on * success. If deleting without error, *vp will be JSVAL_FALSE if obj[id] is * permanent, and JSVAL_TRUE if id named a direct property of obj that was in * fact deleted, or if id names no direct property of obj (id could name a * prototype property, or no property in obj or its prototype chain). */ typedef JSBool (* JS_DLL_CALLBACK JSPropertyIdOp)(JSContext *cx, JSObject *obj, jsid id, jsval *vp); /* * Get or set attributes of the property obj[id]. Return false on error or * exception, true with current attributes in *attrsp. If prop is non-null, * it must come from the *propp out parameter of a prior JSDefinePropOp or * JSLookupPropOp call. */ typedef JSBool (* JS_DLL_CALLBACK JSAttributesOp)(JSContext *cx, JSObject *obj, jsid id, JSProperty *prop, uintN *attrsp); /* * JSObjectOps.checkAccess type: check whether obj[id] may be accessed per * mode, returning false on error/exception, true on success with obj[id]'s * last-got value in *vp, and its attributes in *attrsp. */ typedef JSBool (* JS_DLL_CALLBACK JSCheckAccessIdOp)(JSContext *cx, JSObject *obj, jsid id, JSAccessMode mode, jsval *vp, uintN *attrsp); /* * A generic type for functions mapping an object to another object, or null * if an error or exception was thrown on cx. Used by JSObjectOps.thisObject * at present. */ typedef JSObject * (* JS_DLL_CALLBACK JSObjectOp)(JSContext *cx, JSObject *obj); /* * A generic type for functions taking a context, object, and property, with * no return value. Used by JSObjectOps.dropProperty currently (see above, * JSDefinePropOp and JSLookupPropOp, for the object-locking protocol in which * dropProperty participates). */ typedef void (* JS_DLL_CALLBACK JSPropertyRefOp)(JSContext *cx, JSObject *obj, JSProperty *prop); /* * Function type for JSObjectOps.setProto and JSObjectOps.setParent. These * hooks must check for cycles without deadlocking, and otherwise take special * steps. See jsobj.c, js_SetProtoOrParent, for an example. */ typedef JSBool (* JS_DLL_CALLBACK JSSetObjectSlotOp)(JSContext *cx, JSObject *obj, uint32 slot, JSObject *pobj); /* * Get and set a required slot, one that should already have been allocated. * These operations are infallible, so required slots must be pre-allocated, * or implementations must suppress out-of-memory errors. The native ops * (js_ObjectOps, see jsobj.c) access slots reserved by including a call to * the JSCLASS_HAS_RESERVED_SLOTS(n) macro in the JSClass.flags initializer. * * NB: the slot parameter is a zero-based index into obj->slots[], unlike the * index parameter to the JS_GetReservedSlot and JS_SetReservedSlot API entry * points, which is a zero-based index into the JSCLASS_RESERVED_SLOTS(clasp) * reserved slots that come after the initial well-known slots: proto, parent, * class, and optionally, the private data slot. */ typedef jsval (* JS_DLL_CALLBACK JSGetRequiredSlotOp)(JSContext *cx, JSObject *obj, uint32 slot); typedef JSBool (* JS_DLL_CALLBACK JSSetRequiredSlotOp)(JSContext *cx, JSObject *obj, uint32 slot, jsval v); typedef JSObject * (* JS_DLL_CALLBACK JSGetMethodOp)(JSContext *cx, JSObject *obj, jsid id, jsval *vp); typedef JSBool (* JS_DLL_CALLBACK JSSetMethodOp)(JSContext *cx, JSObject *obj, jsid id, jsval *vp); typedef JSBool (* JS_DLL_CALLBACK JSEnumerateValuesOp)(JSContext *cx, JSObject *obj, JSIterateOp enum_op, jsval *statep, jsid *idp, jsval *vp); typedef JSBool (* JS_DLL_CALLBACK JSEqualityOp)(JSContext *cx, JSObject *obj, jsval v, JSBool *bp); typedef JSBool (* JS_DLL_CALLBACK JSConcatenateOp)(JSContext *cx, JSObject *obj, jsval v, jsval *vp); /* Typedef for native functions called by the JS VM. */ typedef JSBool (* JS_DLL_CALLBACK JSNative)(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval); /* Callbacks and their arguments. */ typedef enum JSContextOp { JSCONTEXT_NEW, JSCONTEXT_DESTROY } JSContextOp; /* * The possible values for contextOp when the runtime calls the callback are: * JSCONTEXT_NEW JS_NewContext succesfully created a new JSContext * instance. The callback can initialize the instance as * required. If the callback returns false, the instance * will be destroyed and JS_NewContext returns null. In * this case the callback is not called again. * JSCONTEXT_DESTROY One of JS_DestroyContext* methods is called. The * callback may perform its own cleanup and must always * return true. * Any other value For future compatibility the callback must do nothing * and return true in this case. */ typedef JSBool (* JS_DLL_CALLBACK JSContextCallback)(JSContext *cx, uintN contextOp); typedef enum JSGCStatus { JSGC_BEGIN, JSGC_END, JSGC_MARK_END, JSGC_FINALIZE_END } JSGCStatus; typedef JSBool (* JS_DLL_CALLBACK JSGCCallback)(JSContext *cx, JSGCStatus status); typedef JSBool (* JS_DLL_CALLBACK JSBranchCallback)(JSContext *cx, JSScript *script); typedef void (* JS_DLL_CALLBACK JSErrorReporter)(JSContext *cx, const char *message, JSErrorReport *report); /* * Possible exception types. These types are part of a JSErrorFormatString * structure. They define which error to throw in case of a runtime error. * JSEXN_NONE marks an unthrowable error. */ typedef enum JSExnType { JSEXN_NONE = -1, JSEXN_ERR, JSEXN_INTERNALERR, JSEXN_EVALERR, JSEXN_RANGEERR, JSEXN_REFERENCEERR, JSEXN_SYNTAXERR, JSEXN_TYPEERR, JSEXN_URIERR, JSEXN_LIMIT } JSExnType; typedef struct JSErrorFormatString { /* The error format string (UTF-8 if JS_C_STRINGS_ARE_UTF8 is defined). */ const char *format; /* The number of arguments to expand in the formatted error message. */ uint16 argCount; /* One of the JSExnType constants above. */ int16 exnType; } JSErrorFormatString; typedef const JSErrorFormatString * (* JS_DLL_CALLBACK JSErrorCallback)(void *userRef, const char *locale, const uintN errorNumber); #ifdef va_start #define JS_ARGUMENT_FORMATTER_DEFINED 1 typedef JSBool (* JS_DLL_CALLBACK JSArgumentFormatter)(JSContext *cx, const char *format, JSBool fromJS, jsval **vpp, va_list *app); #endif typedef JSBool (* JS_DLL_CALLBACK JSLocaleToUpperCase)(JSContext *cx, JSString *src, jsval *rval); typedef JSBool (* JS_DLL_CALLBACK JSLocaleToLowerCase)(JSContext *cx, JSString *src, jsval *rval); typedef JSBool (* JS_DLL_CALLBACK JSLocaleCompare)(JSContext *cx, JSString *src1, JSString *src2, jsval *rval); typedef JSBool (* JS_DLL_CALLBACK JSLocaleToUnicode)(JSContext *cx, char *src, jsval *rval); /* * Security protocol types. */ typedef struct JSPrincipals JSPrincipals; /* * XDR-encode or -decode a principals instance, based on whether xdr->mode is * JSXDR_ENCODE, in which case *principalsp should be encoded; or JSXDR_DECODE, * in which case implementations must return a held (via JSPRINCIPALS_HOLD), * non-null *principalsp out parameter. Return true on success, false on any * error, which the implementation must have reported. */ typedef JSBool (* JS_DLL_CALLBACK JSPrincipalsTranscoder)(JSXDRState *xdr, JSPrincipals **principalsp); /* * Return a weak reference to the principals associated with obj, possibly via * the immutable parent chain leading from obj to a top-level container (e.g., * a window object in the DOM level 0). If there are no principals associated * with obj, return null. Therefore null does not mean an error was reported; * in no event should an error be reported or an exception be thrown by this * callback's implementation. */ typedef JSPrincipals * (* JS_DLL_CALLBACK JSObjectPrincipalsFinder)(JSContext *cx, JSObject *obj); JS_END_EXTERN_C #endif /* jspubtd_h___ */ pacparser-1.4.5/src/spidermonkey/js/src/jsregexp.c000066400000000000000000004330551464010763600222200ustar00rootroot00000000000000/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- * vim: set sw=4 ts=8 et tw=78: * * ***** BEGIN LICENSE BLOCK ***** * Version: MPL 1.1/GPL 2.0/LGPL 2.1 * * The contents of this file are subject to the Mozilla Public License Version * 1.1 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * http://www.mozilla.org/MPL/ * * Software distributed under the License is distributed on an "AS IS" basis, * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License * for the specific language governing rights and limitations under the * License. * * The Original Code is Mozilla Communicator client code, released * March 31, 1998. * * The Initial Developer of the Original Code is * Netscape Communications Corporation. * Portions created by the Initial Developer are Copyright (C) 1998 * the Initial Developer. All Rights Reserved. * * Contributor(s): * * Alternatively, the contents of this file may be used under the terms of * either of the GNU General Public License Version 2 or later (the "GPL"), * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), * in which case the provisions of the GPL or the LGPL are applicable instead * of those above. If you wish to allow use of your version of this file only * under the terms of either the GPL or the LGPL, and not to allow others to * use your version of this file under the terms of the MPL, indicate your * decision by deleting the provisions above and replace them with the notice * and other provisions required by the GPL or the LGPL. If you do not delete * the provisions above, a recipient may use your version of this file under * the terms of any one of the MPL, the GPL or the LGPL. * * ***** END LICENSE BLOCK ***** */ /* * JS regular expressions, after Perl. */ #include "jsstddef.h" #include #include #include "jstypes.h" #include "jsarena.h" /* Added by JSIFY */ #include "jsutil.h" /* Added by JSIFY */ #include "jsapi.h" #include "jsarray.h" #include "jsatom.h" #include "jscntxt.h" #include "jsconfig.h" #include "jsfun.h" #include "jsgc.h" #include "jsinterp.h" #include "jslock.h" #include "jsnum.h" #include "jsobj.h" #include "jsopcode.h" #include "jsregexp.h" #include "jsscan.h" #include "jsstr.h" /* Note : contiguity of 'simple opcodes' is important for SimpleMatch() */ typedef enum REOp { REOP_EMPTY = 0, /* match rest of input against rest of r.e. */ REOP_ALT = 1, /* alternative subexpressions in kid and next */ REOP_SIMPLE_START = 2, /* start of 'simple opcodes' */ REOP_BOL = 2, /* beginning of input (or line if multiline) */ REOP_EOL = 3, /* end of input (or line if multiline) */ REOP_WBDRY = 4, /* match "" at word boundary */ REOP_WNONBDRY = 5, /* match "" at word non-boundary */ REOP_DOT = 6, /* stands for any character */ REOP_DIGIT = 7, /* match a digit char: [0-9] */ REOP_NONDIGIT = 8, /* match a non-digit char: [^0-9] */ REOP_ALNUM = 9, /* match an alphanumeric char: [0-9a-z_A-Z] */ REOP_NONALNUM = 10, /* match a non-alphanumeric char: [^0-9a-z_A-Z] */ REOP_SPACE = 11, /* match a whitespace char */ REOP_NONSPACE = 12, /* match a non-whitespace char */ REOP_BACKREF = 13, /* back-reference (e.g., \1) to a parenthetical */ REOP_FLAT = 14, /* match a flat string */ REOP_FLAT1 = 15, /* match a single char */ REOP_FLATi = 16, /* case-independent REOP_FLAT */ REOP_FLAT1i = 17, /* case-independent REOP_FLAT1 */ REOP_UCFLAT1 = 18, /* single Unicode char */ REOP_UCFLAT1i = 19, /* case-independent REOP_UCFLAT1 */ REOP_UCFLAT = 20, /* flat Unicode string; len immediate counts chars */ REOP_UCFLATi = 21, /* case-independent REOP_UCFLAT */ REOP_CLASS = 22, /* character class with index */ REOP_NCLASS = 23, /* negated character class with index */ REOP_SIMPLE_END = 23, /* end of 'simple opcodes' */ REOP_QUANT = 25, /* quantified atom: atom{1,2} */ REOP_STAR = 26, /* zero or more occurrences of kid */ REOP_PLUS = 27, /* one or more occurrences of kid */ REOP_OPT = 28, /* optional subexpression in kid */ REOP_LPAREN = 29, /* left paren bytecode: kid is u.num'th sub-regexp */ REOP_RPAREN = 30, /* right paren bytecode */ REOP_JUMP = 31, /* for deoptimized closure loops */ REOP_DOTSTAR = 32, /* optimize .* to use a single opcode */ REOP_ANCHOR = 33, /* like .* but skips left context to unanchored r.e. */ REOP_EOLONLY = 34, /* $ not preceded by any pattern */ REOP_BACKREFi = 37, /* case-independent REOP_BACKREF */ REOP_LPARENNON = 41, /* non-capturing version of REOP_LPAREN */ REOP_ASSERT = 43, /* zero width positive lookahead assertion */ REOP_ASSERT_NOT = 44, /* zero width negative lookahead assertion */ REOP_ASSERTTEST = 45, /* sentinel at end of assertion child */ REOP_ASSERTNOTTEST = 46, /* sentinel at end of !assertion child */ REOP_MINIMALSTAR = 47, /* non-greedy version of * */ REOP_MINIMALPLUS = 48, /* non-greedy version of + */ REOP_MINIMALOPT = 49, /* non-greedy version of ? */ REOP_MINIMALQUANT = 50, /* non-greedy version of {} */ REOP_ENDCHILD = 51, /* sentinel at end of quantifier child */ REOP_REPEAT = 52, /* directs execution of greedy quantifier */ REOP_MINIMALREPEAT = 53, /* directs execution of non-greedy quantifier */ REOP_ALTPREREQ = 54, /* prerequisite for ALT, either of two chars */ REOP_ALTPREREQ2 = 55, /* prerequisite for ALT, a char or a class */ REOP_ENDALT = 56, /* end of final alternate */ REOP_CONCAT = 57, /* concatenation of terms (parse time only) */ REOP_END } REOp; #define REOP_IS_SIMPLE(op) ((unsigned)((op) - REOP_SIMPLE_START) < \ (unsigned)REOP_SIMPLE_END) struct RENode { REOp op; /* r.e. op bytecode */ RENode *next; /* next in concatenation order */ void *kid; /* first operand */ union { void *kid2; /* second operand */ jsint num; /* could be a number */ size_t parenIndex; /* or a parenthesis index */ struct { /* or a quantifier range */ uintN min; uintN max; JSPackedBool greedy; } range; struct { /* or a character class */ size_t startIndex; size_t kidlen; /* length of string at kid, in jschars */ size_t index; /* index into class list */ uint16 bmsize; /* bitmap size, based on max char code */ JSPackedBool sense; } ucclass; struct { /* or a literal sequence */ jschar chr; /* of one character */ size_t length; /* or many (via the kid) */ } flat; struct { RENode *kid2; /* second operand from ALT */ jschar ch1; /* match char for ALTPREREQ */ jschar ch2; /* ditto, or class index for ALTPREREQ2 */ } altprereq; } u; }; #define RE_IS_LETTER(c) (((c >= 'A') && (c <= 'Z')) || \ ((c >= 'a') && (c <= 'z')) ) #define RE_IS_LINE_TERM(c) ((c == '\n') || (c == '\r') || \ (c == LINE_SEPARATOR) || (c == PARA_SEPARATOR)) #define CLASS_CACHE_SIZE 4 typedef struct CompilerState { JSContext *context; JSTokenStream *tokenStream; /* For reporting errors */ const jschar *cpbegin; const jschar *cpend; const jschar *cp; size_t parenCount; size_t classCount; /* number of [] encountered */ size_t treeDepth; /* maximum depth of parse tree */ size_t progLength; /* estimated bytecode length */ RENode *result; size_t classBitmapsMem; /* memory to hold all class bitmaps */ struct { const jschar *start; /* small cache of class strings */ size_t length; /* since they're often the same */ size_t index; } classCache[CLASS_CACHE_SIZE]; uint16 flags; } CompilerState; typedef struct EmitStateStackEntry { jsbytecode *altHead; /* start of REOP_ALT* opcode */ jsbytecode *nextAltFixup; /* fixup pointer to next-alt offset */ jsbytecode *nextTermFixup; /* fixup ptr. to REOP_JUMP offset */ jsbytecode *endTermFixup; /* fixup ptr. to REOPT_ALTPREREQ* offset */ RENode *continueNode; /* original REOP_ALT* node being stacked */ jsbytecode continueOp; /* REOP_JUMP or REOP_ENDALT continuation */ JSPackedBool jumpToJumpFlag; /* true if we've patched jump-to-jump to avoid 16-bit unsigned offset overflow */ } EmitStateStackEntry; /* * Immediate operand sizes and getter/setters. Unlike the ones in jsopcode.h, * the getters and setters take the pc of the offset, not of the opcode before * the offset. */ #define ARG_LEN 2 #define GET_ARG(pc) ((uint16)(((pc)[0] << 8) | (pc)[1])) #define SET_ARG(pc, arg) ((pc)[0] = (jsbytecode) ((arg) >> 8), \ (pc)[1] = (jsbytecode) (arg)) #define OFFSET_LEN ARG_LEN #define OFFSET_MAX (JS_BIT(ARG_LEN * 8) - 1) #define GET_OFFSET(pc) GET_ARG(pc) /* * Maximum supported tree depth is maximum size of EmitStateStackEntry stack. * For sanity, we limit it to 2^24 bytes. */ #define TREE_DEPTH_MAX (JS_BIT(24) / sizeof(EmitStateStackEntry)) /* * The maximum memory that can be allocated for class bitmaps. * For sanity, we limit it to 2^24 bytes. */ #define CLASS_BITMAPS_MEM_LIMIT JS_BIT(24) /* * Functions to get size and write/read bytecode that represent small indexes * compactly. * Each byte in the code represent 7-bit chunk of the index. 8th bit when set * indicates that the following byte brings more bits to the index. Otherwise * this is the last byte in the index bytecode representing highest index bits. */ static size_t GetCompactIndexWidth(size_t index) { size_t width; for (width = 1; (index >>= 7) != 0; ++width) { } return width; } static jsbytecode * WriteCompactIndex(jsbytecode *pc, size_t index) { size_t next; while ((next = index >> 7) != 0) { *pc++ = (jsbytecode)(index | 0x80); index = next; } *pc++ = (jsbytecode)index; return pc; } static jsbytecode * ReadCompactIndex(jsbytecode *pc, size_t *result) { size_t nextByte; nextByte = *pc++; if ((nextByte & 0x80) == 0) { /* * Short-circuit the most common case when compact index <= 127. */ *result = nextByte; } else { size_t shift = 7; *result = 0x7F & nextByte; do { nextByte = *pc++; *result |= (nextByte & 0x7F) << shift; shift += 7; } while ((nextByte & 0x80) != 0); } return pc; } typedef struct RECapture { ptrdiff_t index; /* start of contents, -1 for empty */ size_t length; /* length of capture */ } RECapture; typedef struct REMatchState { const jschar *cp; RECapture parens[1]; /* first of 're->parenCount' captures, allocated at end of this struct */ } REMatchState; struct REBackTrackData; typedef struct REProgState { jsbytecode *continue_pc; /* current continuation data */ jsbytecode continue_op; ptrdiff_t index; /* progress in text */ size_t parenSoFar; /* highest indexed paren started */ union { struct { uintN min; /* current quantifier limits */ uintN max; } quantifier; struct { size_t top; /* backtrack stack state */ size_t sz; } assertion; } u; } REProgState; typedef struct REBackTrackData { size_t sz; /* size of previous stack entry */ jsbytecode *backtrack_pc; /* where to backtrack to */ jsbytecode backtrack_op; const jschar *cp; /* index in text of match at backtrack */ size_t parenIndex; /* start index of saved paren contents */ size_t parenCount; /* # of saved paren contents */ size_t saveStateStackTop; /* number of parent states */ /* saved parent states follow */ /* saved paren contents follow */ } REBackTrackData; #define INITIAL_STATESTACK 100 #define INITIAL_BACKTRACK 8000 typedef struct REGlobalData { JSContext *cx; JSRegExp *regexp; /* the RE in execution */ JSBool ok; /* runtime error (out_of_memory only?) */ size_t start; /* offset to start at */ ptrdiff_t skipped; /* chars skipped anchoring this r.e. */ const jschar *cpbegin; /* text base address */ const jschar *cpend; /* text limit address */ REProgState *stateStack; /* stack of state of current parents */ size_t stateStackTop; size_t stateStackLimit; REBackTrackData *backTrackStack;/* stack of matched-so-far positions */ REBackTrackData *backTrackSP; size_t backTrackStackSize; size_t cursz; /* size of current stack entry */ JSArenaPool pool; /* It's faster to use one malloc'd pool than to malloc/free the three items that are allocated from this pool */ } REGlobalData; /* * 1. If IgnoreCase is false, return ch. * 2. Let u be ch converted to upper case as if by calling * String.prototype.toUpperCase on the one-character string ch. * 3. If u does not consist of a single character, return ch. * 4. Let cu be u's character. * 5. If ch's code point value is greater than or equal to decimal 128 and cu's * code point value is less than decimal 128, then return ch. * 6. Return cu. */ static jschar upcase(jschar ch) { jschar cu = JS_TOUPPER(ch); if (ch >= 128 && cu < 128) return ch; return cu; } static jschar downcase(jschar ch) { jschar cl = JS_TOLOWER(ch); if (cl >= 128 && ch < 128) return ch; return cl; } /* Construct and initialize an RENode, returning NULL for out-of-memory */ static RENode * NewRENode(CompilerState *state, REOp op) { JSContext *cx; RENode *ren; cx = state->context; JS_ARENA_ALLOCATE_CAST(ren, RENode *, &cx->tempPool, sizeof *ren); if (!ren) { JS_ReportOutOfMemory(cx); return NULL; } ren->op = op; ren->next = NULL; ren->kid = NULL; return ren; } /* * Validates and converts hex ascii value. */ static JSBool isASCIIHexDigit(jschar c, uintN *digit) { uintN cv = c; if (cv < '0') return JS_FALSE; if (cv <= '9') { *digit = cv - '0'; return JS_TRUE; } cv |= 0x20; if (cv >= 'a' && cv <= 'f') { *digit = cv - 'a' + 10; return JS_TRUE; } return JS_FALSE; } typedef struct { REOp op; const jschar *errPos; size_t parenIndex; } REOpData; /* * Process the op against the two top operands, reducing them to a single * operand in the penultimate slot. Update progLength and treeDepth. */ static JSBool ProcessOp(CompilerState *state, REOpData *opData, RENode **operandStack, intN operandSP) { RENode *result; switch (opData->op) { case REOP_ALT: result = NewRENode(state, REOP_ALT); if (!result) return JS_FALSE; result->kid = operandStack[operandSP - 2]; result->u.kid2 = operandStack[operandSP - 1]; operandStack[operandSP - 2] = result; if (state->treeDepth == TREE_DEPTH_MAX) { js_ReportCompileErrorNumber(state->context, state->tokenStream, JSREPORT_TS | JSREPORT_ERROR, JSMSG_REGEXP_TOO_COMPLEX); return JS_FALSE; } ++state->treeDepth; /* * Look at both alternates to see if there's a FLAT or a CLASS at * the start of each. If so, use a prerequisite match. */ if (((RENode *) result->kid)->op == REOP_FLAT && ((RENode *) result->u.kid2)->op == REOP_FLAT && (state->flags & JSREG_FOLD) == 0) { result->op = REOP_ALTPREREQ; result->u.altprereq.ch1 = ((RENode *) result->kid)->u.flat.chr; result->u.altprereq.ch2 = ((RENode *) result->u.kid2)->u.flat.chr; /* ALTPREREQ, , uch1, uch2, , ..., JUMP, ... ENDALT */ state->progLength += 13; } else if (((RENode *) result->kid)->op == REOP_CLASS && ((RENode *) result->kid)->u.ucclass.index < 256 && ((RENode *) result->u.kid2)->op == REOP_FLAT && (state->flags & JSREG_FOLD) == 0) { result->op = REOP_ALTPREREQ2; result->u.altprereq.ch1 = ((RENode *) result->u.kid2)->u.flat.chr; result->u.altprereq.ch2 = ((RENode *) result->kid)->u.ucclass.index; /* ALTPREREQ2, , uch1, uch2, , ..., JUMP, ... ENDALT */ state->progLength += 13; } else if (((RENode *) result->kid)->op == REOP_FLAT && ((RENode *) result->u.kid2)->op == REOP_CLASS && ((RENode *) result->u.kid2)->u.ucclass.index < 256 && (state->flags & JSREG_FOLD) == 0) { result->op = REOP_ALTPREREQ2; result->u.altprereq.ch1 = ((RENode *) result->kid)->u.flat.chr; result->u.altprereq.ch2 = ((RENode *) result->u.kid2)->u.ucclass.index; /* ALTPREREQ2, , uch1, uch2, , ..., JUMP, ... ENDALT */ state->progLength += 13; } else { /* ALT, , ..., JUMP, ... ENDALT */ state->progLength += 7; } break; case REOP_CONCAT: result = operandStack[operandSP - 2]; while (result->next) result = result->next; result->next = operandStack[operandSP - 1]; break; case REOP_ASSERT: case REOP_ASSERT_NOT: case REOP_LPARENNON: case REOP_LPAREN: /* These should have been processed by a close paren. */ js_ReportCompileErrorNumberUC(state->context, state->tokenStream, JSREPORT_TS | JSREPORT_ERROR, JSMSG_MISSING_PAREN, opData->errPos); return JS_FALSE; default:; } return JS_TRUE; } /* * Parser forward declarations. */ static JSBool ParseTerm(CompilerState *state); static JSBool ParseQuantifier(CompilerState *state); static intN ParseMinMaxQuantifier(CompilerState *state, JSBool ignoreValues); /* * Top-down regular expression grammar, based closely on Perl4. * * regexp: altern A regular expression is one or more * altern '|' regexp alternatives separated by vertical bar. */ #define INITIAL_STACK_SIZE 128 static JSBool ParseRegExp(CompilerState *state) { size_t parenIndex; RENode *operand; REOpData *operatorStack; RENode **operandStack; REOp op; intN i; JSBool result = JS_FALSE; intN operatorSP = 0, operatorStackSize = INITIAL_STACK_SIZE; intN operandSP = 0, operandStackSize = INITIAL_STACK_SIZE; /* Watch out for empty regexp */ if (state->cp == state->cpend) { state->result = NewRENode(state, REOP_EMPTY); return (state->result != NULL); } operatorStack = (REOpData *) JS_malloc(state->context, sizeof(REOpData) * operatorStackSize); if (!operatorStack) return JS_FALSE; operandStack = (RENode **) JS_malloc(state->context, sizeof(RENode *) * operandStackSize); if (!operandStack) goto out; for (;;) { parenIndex = state->parenCount; if (state->cp == state->cpend) { /* * If we are at the end of the regexp and we're short one or more * operands, the regexp must have the form /x|/ or some such, with * left parentheses making us short more than one operand. */ if (operatorSP >= operandSP) { operand = NewRENode(state, REOP_EMPTY); if (!operand) goto out; goto pushOperand; } } else { switch (*state->cp) { case '(': ++state->cp; if (state->cp + 1 < state->cpend && *state->cp == '?' && (state->cp[1] == '=' || state->cp[1] == '!' || state->cp[1] == ':')) { switch (state->cp[1]) { case '=': op = REOP_ASSERT; /* ASSERT, , ... ASSERTTEST */ state->progLength += 4; break; case '!': op = REOP_ASSERT_NOT; /* ASSERTNOT, , ... ASSERTNOTTEST */ state->progLength += 4; break; default: op = REOP_LPARENNON; break; } state->cp += 2; } else { op = REOP_LPAREN; /* LPAREN, , ... RPAREN, */ state->progLength += 2 * (1 + GetCompactIndexWidth(parenIndex)); state->parenCount++; if (state->parenCount == 65535) { js_ReportCompileErrorNumber(state->context, state->tokenStream, JSREPORT_TS | JSREPORT_ERROR, JSMSG_TOO_MANY_PARENS); goto out; } } goto pushOperator; case ')': /* * If there's no stacked open parenthesis, throw syntax error. */ for (i = operatorSP - 1; ; i--) { if (i < 0) { js_ReportCompileErrorNumber(state->context, state->tokenStream, JSREPORT_TS | JSREPORT_ERROR, JSMSG_UNMATCHED_RIGHT_PAREN); goto out; } if (operatorStack[i].op == REOP_ASSERT || operatorStack[i].op == REOP_ASSERT_NOT || operatorStack[i].op == REOP_LPARENNON || operatorStack[i].op == REOP_LPAREN) { break; } } /* FALL THROUGH */ case '|': /* Expected an operand before these, so make an empty one */ operand = NewRENode(state, REOP_EMPTY); if (!operand) goto out; goto pushOperand; default: if (!ParseTerm(state)) goto out; operand = state->result; pushOperand: if (operandSP == operandStackSize) { operandStackSize += operandStackSize; operandStack = (RENode **) JS_realloc(state->context, operandStack, sizeof(RENode *) * operandStackSize); if (!operandStack) goto out; } operandStack[operandSP++] = operand; break; } } /* At the end; process remaining operators. */ restartOperator: if (state->cp == state->cpend) { while (operatorSP) { --operatorSP; if (!ProcessOp(state, &operatorStack[operatorSP], operandStack, operandSP)) goto out; --operandSP; } JS_ASSERT(operandSP == 1); state->result = operandStack[0]; result = JS_TRUE; goto out; } switch (*state->cp) { case '|': /* Process any stacked 'concat' operators */ ++state->cp; while (operatorSP && operatorStack[operatorSP - 1].op == REOP_CONCAT) { --operatorSP; if (!ProcessOp(state, &operatorStack[operatorSP], operandStack, operandSP)) { goto out; } --operandSP; } op = REOP_ALT; goto pushOperator; case ')': /* * If there's no stacked open parenthesis, throw syntax error. */ for (i = operatorSP - 1; ; i--) { if (i < 0) { js_ReportCompileErrorNumber(state->context, state->tokenStream, JSREPORT_TS | JSREPORT_ERROR, JSMSG_UNMATCHED_RIGHT_PAREN); goto out; } if (operatorStack[i].op == REOP_ASSERT || operatorStack[i].op == REOP_ASSERT_NOT || operatorStack[i].op == REOP_LPARENNON || operatorStack[i].op == REOP_LPAREN) { break; } } ++state->cp; /* Process everything on the stack until the open parenthesis. */ for (;;) { JS_ASSERT(operatorSP); --operatorSP; switch (operatorStack[operatorSP].op) { case REOP_ASSERT: case REOP_ASSERT_NOT: case REOP_LPAREN: operand = NewRENode(state, operatorStack[operatorSP].op); if (!operand) goto out; operand->u.parenIndex = operatorStack[operatorSP].parenIndex; JS_ASSERT(operandSP); operand->kid = operandStack[operandSP - 1]; operandStack[operandSP - 1] = operand; if (state->treeDepth == TREE_DEPTH_MAX) { js_ReportCompileErrorNumber(state->context, state->tokenStream, JSREPORT_TS | JSREPORT_ERROR, JSMSG_REGEXP_TOO_COMPLEX); goto out; } ++state->treeDepth; /* FALL THROUGH */ case REOP_LPARENNON: state->result = operandStack[operandSP - 1]; if (!ParseQuantifier(state)) goto out; operandStack[operandSP - 1] = state->result; goto restartOperator; default: if (!ProcessOp(state, &operatorStack[operatorSP], operandStack, operandSP)) goto out; --operandSP; break; } } break; case '{': { const jschar *errp = state->cp; if (ParseMinMaxQuantifier(state, JS_TRUE) < 0) { /* * This didn't even scan correctly as a quantifier, so we should * treat it as flat. */ op = REOP_CONCAT; goto pushOperator; } state->cp = errp; /* FALL THROUGH */ } case '+': case '*': case '?': js_ReportCompileErrorNumberUC(state->context, state->tokenStream, JSREPORT_TS | JSREPORT_ERROR, JSMSG_BAD_QUANTIFIER, state->cp); result = JS_FALSE; goto out; default: /* Anything else is the start of the next term. */ op = REOP_CONCAT; pushOperator: if (operatorSP == operatorStackSize) { operatorStackSize += operatorStackSize; operatorStack = (REOpData *) JS_realloc(state->context, operatorStack, sizeof(REOpData) * operatorStackSize); if (!operatorStack) goto out; } operatorStack[operatorSP].op = op; operatorStack[operatorSP].errPos = state->cp; operatorStack[operatorSP++].parenIndex = parenIndex; break; } } out: if (operatorStack) JS_free(state->context, operatorStack); if (operandStack) JS_free(state->context, operandStack); return result; } /* * Hack two bits in CompilerState.flags, for use within FindParenCount to flag * its being on the stack, and to propagate errors to its callers. */ #define JSREG_FIND_PAREN_COUNT 0x8000 #define JSREG_FIND_PAREN_ERROR 0x4000 /* * Magic return value from FindParenCount and GetDecimalValue, to indicate * overflow beyond GetDecimalValue's max parameter, or a computed maximum if * its findMax parameter is non-null. */ #define OVERFLOW_VALUE ((uintN)-1) static uintN FindParenCount(CompilerState *state) { CompilerState temp; int i; if (state->flags & JSREG_FIND_PAREN_COUNT) return OVERFLOW_VALUE; /* * Copy state into temp, flag it so we never report an invalid backref, * and reset its members to parse the entire regexp. This is obviously * suboptimal, but GetDecimalValue calls us only if a backref appears to * refer to a forward parenthetical, which is rare. */ temp = *state; temp.flags |= JSREG_FIND_PAREN_COUNT; temp.cp = temp.cpbegin; temp.parenCount = 0; temp.classCount = 0; temp.progLength = 0; temp.treeDepth = 0; temp.classBitmapsMem = 0; for (i = 0; i < CLASS_CACHE_SIZE; i++) temp.classCache[i].start = NULL; if (!ParseRegExp(&temp)) { state->flags |= JSREG_FIND_PAREN_ERROR; return OVERFLOW_VALUE; } return temp.parenCount; } /* * Extract and return a decimal value at state->cp. The initial character c * has already been read. Return OVERFLOW_VALUE if the result exceeds max. * Callers who pass a non-null findMax should test JSREG_FIND_PAREN_ERROR in * state->flags to discover whether an error occurred under findMax. */ static uintN GetDecimalValue(jschar c, uintN max, uintN (*findMax)(CompilerState *state), CompilerState *state) { uintN value = JS7_UNDEC(c); JSBool overflow = (value > max && (!findMax || value > findMax(state))); /* The following restriction allows simpler overflow checks. */ JS_ASSERT(max <= ((uintN)-1 - 9) / 10); while (state->cp < state->cpend) { c = *state->cp; if (!JS7_ISDEC(c)) break; value = 10 * value + JS7_UNDEC(c); if (!overflow && value > max && (!findMax || value > findMax(state))) overflow = JS_TRUE; ++state->cp; } return overflow ? OVERFLOW_VALUE : value; } /* * Calculate the total size of the bitmap required for a class expression. */ static JSBool CalculateBitmapSize(CompilerState *state, RENode *target, const jschar *src, const jschar *end) { uintN max = 0; JSBool inRange = JS_FALSE; jschar c, rangeStart = 0; uintN n, digit, nDigits, i; target->u.ucclass.bmsize = 0; target->u.ucclass.sense = JS_TRUE; if (src == end) return JS_TRUE; if (*src == '^') { ++src; target->u.ucclass.sense = JS_FALSE; } while (src != end) { uintN localMax = 0; switch (*src) { case '\\': ++src; c = *src++; switch (c) { case 'b': localMax = 0x8; break; case 'f': localMax = 0xC; break; case 'n': localMax = 0xA; break; case 'r': localMax = 0xD; break; case 't': localMax = 0x9; break; case 'v': localMax = 0xB; break; case 'c': if (src < end && RE_IS_LETTER(*src)) { localMax = (jschar) (*src++ & 0x1F); } else { --src; localMax = '\\'; } break; case 'x': nDigits = 2; goto lexHex; case 'u': nDigits = 4; lexHex: n = 0; for (i = 0; (i < nDigits) && (src < end); i++) { c = *src++; if (!isASCIIHexDigit(c, &digit)) { /* * Back off to accepting the original *'\' as a literal. */ src -= i + 1; n = '\\'; break; } n = (n << 4) | digit; } localMax = n; break; case 'd': if (inRange) { JS_ReportErrorNumber(state->context, js_GetErrorMessage, NULL, JSMSG_BAD_CLASS_RANGE); return JS_FALSE; } localMax = '9'; break; case 'D': case 's': case 'S': case 'w': case 'W': if (inRange) { JS_ReportErrorNumber(state->context, js_GetErrorMessage, NULL, JSMSG_BAD_CLASS_RANGE); return JS_FALSE; } target->u.ucclass.bmsize = 65535; return JS_TRUE; case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': /* * This is a non-ECMA extension - decimal escapes (in this * case, octal!) are supposed to be an error inside class * ranges, but supported here for backwards compatibility. * */ n = JS7_UNDEC(c); c = *src; if ('0' <= c && c <= '7') { src++; n = 8 * n + JS7_UNDEC(c); c = *src; if ('0' <= c && c <= '7') { src++; i = 8 * n + JS7_UNDEC(c); if (i <= 0377) n = i; else src--; } } localMax = n; break; default: localMax = c; break; } break; default: localMax = *src++; break; } if (state->flags & JSREG_FOLD) { c = JS_MAX(upcase((jschar) localMax), downcase((jschar) localMax)); if (c > localMax) localMax = c; } if (inRange) { if (rangeStart > localMax) { JS_ReportErrorNumber(state->context, js_GetErrorMessage, NULL, JSMSG_BAD_CLASS_RANGE); return JS_FALSE; } inRange = JS_FALSE; } else { if (src < end - 1) { if (*src == '-') { ++src; inRange = JS_TRUE; rangeStart = (jschar)localMax; continue; } } } if (localMax > max) max = localMax; } target->u.ucclass.bmsize = max; return JS_TRUE; } /* * item: assertion An item is either an assertion or * quantatom a quantified atom. * * assertion: '^' Assertions match beginning of string * (or line if the class static property * RegExp.multiline is true). * '$' End of string (or line if the class * static property RegExp.multiline is * true). * '\b' Word boundary (between \w and \W). * '\B' Word non-boundary. * * quantatom: atom An unquantified atom. * quantatom '{' n ',' m '}' * Atom must occur between n and m times. * quantatom '{' n ',' '}' Atom must occur at least n times. * quantatom '{' n '}' Atom must occur exactly n times. * quantatom '*' Zero or more times (same as {0,}). * quantatom '+' One or more times (same as {1,}). * quantatom '?' Zero or one time (same as {0,1}). * * any of which can be optionally followed by '?' for ungreedy * * atom: '(' regexp ')' A parenthesized regexp (what matched * can be addressed using a backreference, * see '\' n below). * '.' Matches any char except '\n'. * '[' classlist ']' A character class. * '[' '^' classlist ']' A negated character class. * '\f' Form Feed. * '\n' Newline (Line Feed). * '\r' Carriage Return. * '\t' Horizontal Tab. * '\v' Vertical Tab. * '\d' A digit (same as [0-9]). * '\D' A non-digit. * '\w' A word character, [0-9a-z_A-Z]. * '\W' A non-word character. * '\s' A whitespace character, [ \b\f\n\r\t\v]. * '\S' A non-whitespace character. * '\' n A backreference to the nth (n decimal * and positive) parenthesized expression. * '\' octal An octal escape sequence (octal must be * two or three digits long, unless it is * 0 for the null character). * '\x' hex A hex escape (hex must be two digits). * '\u' unicode A unicode escape (must be four digits). * '\c' ctrl A control character, ctrl is a letter. * '\' literalatomchar Any character except one of the above * that follow '\' in an atom. * otheratomchar Any character not first among the other * atom right-hand sides. */ static JSBool ParseTerm(CompilerState *state) { jschar c = *state->cp++; uintN nDigits; uintN num, tmp, n, i; const jschar *termStart; switch (c) { /* assertions and atoms */ case '^': state->result = NewRENode(state, REOP_BOL); if (!state->result) return JS_FALSE; state->progLength++; return JS_TRUE; case '$': state->result = NewRENode(state, REOP_EOL); if (!state->result) return JS_FALSE; state->progLength++; return JS_TRUE; case '\\': if (state->cp >= state->cpend) { /* a trailing '\' is an error */ js_ReportCompileErrorNumber(state->context, state->tokenStream, JSREPORT_TS | JSREPORT_ERROR, JSMSG_TRAILING_SLASH); return JS_FALSE; } c = *state->cp++; switch (c) { /* assertion escapes */ case 'b' : state->result = NewRENode(state, REOP_WBDRY); if (!state->result) return JS_FALSE; state->progLength++; return JS_TRUE; case 'B': state->result = NewRENode(state, REOP_WNONBDRY); if (!state->result) return JS_FALSE; state->progLength++; return JS_TRUE; /* Decimal escape */ case '0': /* Give a strict warning. See also the note below. */ if (!js_ReportCompileErrorNumber(state->context, state->tokenStream, JSREPORT_TS | JSREPORT_WARNING | JSREPORT_STRICT, JSMSG_INVALID_BACKREF)) { return JS_FALSE; } doOctal: num = 0; while (state->cp < state->cpend) { c = *state->cp; if (c < '0' || '7' < c) break; state->cp++; tmp = 8 * num + (uintN)JS7_UNDEC(c); if (tmp > 0377) break; num = tmp; } c = (jschar)num; doFlat: state->result = NewRENode(state, REOP_FLAT); if (!state->result) return JS_FALSE; state->result->u.flat.chr = c; state->result->u.flat.length = 1; state->progLength += 3; break; case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': termStart = state->cp - 1; num = GetDecimalValue(c, state->parenCount, FindParenCount, state); if (state->flags & JSREG_FIND_PAREN_ERROR) return JS_FALSE; if (num == OVERFLOW_VALUE) { /* Give a strict mode warning. */ if (!js_ReportCompileErrorNumber(state->context, state->tokenStream, JSREPORT_TS | JSREPORT_WARNING | JSREPORT_STRICT, (c >= '8') ? JSMSG_INVALID_BACKREF : JSMSG_BAD_BACKREF)) { return JS_FALSE; } /* * Note: ECMA 262, 15.10.2.9 says that we should throw a syntax * error here. However, for compatibility with IE, we treat the * whole backref as flat if the first character in it is not a * valid octal character, and as an octal escape otherwise. */ state->cp = termStart; if (c >= '8') { /* Treat this as flat. termStart - 1 is the \. */ c = '\\'; goto asFlat; } /* Treat this as an octal escape. */ goto doOctal; } JS_ASSERT(1 <= num && num <= 0x10000); state->result = NewRENode(state, REOP_BACKREF); if (!state->result) return JS_FALSE; state->result->u.parenIndex = num - 1; state->progLength += 1 + GetCompactIndexWidth(state->result->u.parenIndex); break; /* Control escape */ case 'f': c = 0xC; goto doFlat; case 'n': c = 0xA; goto doFlat; case 'r': c = 0xD; goto doFlat; case 't': c = 0x9; goto doFlat; case 'v': c = 0xB; goto doFlat; /* Control letter */ case 'c': if (state->cp < state->cpend && RE_IS_LETTER(*state->cp)) { c = (jschar) (*state->cp++ & 0x1F); } else { /* back off to accepting the original '\' as a literal */ --state->cp; c = '\\'; } goto doFlat; /* HexEscapeSequence */ case 'x': nDigits = 2; goto lexHex; /* UnicodeEscapeSequence */ case 'u': nDigits = 4; lexHex: n = 0; for (i = 0; i < nDigits && state->cp < state->cpend; i++) { uintN digit; c = *state->cp++; if (!isASCIIHexDigit(c, &digit)) { /* * Back off to accepting the original 'u' or 'x' as a * literal. */ state->cp -= i + 2; n = *state->cp++; break; } n = (n << 4) | digit; } c = (jschar) n; goto doFlat; /* Character class escapes */ case 'd': state->result = NewRENode(state, REOP_DIGIT); doSimple: if (!state->result) return JS_FALSE; state->progLength++; break; case 'D': state->result = NewRENode(state, REOP_NONDIGIT); goto doSimple; case 's': state->result = NewRENode(state, REOP_SPACE); goto doSimple; case 'S': state->result = NewRENode(state, REOP_NONSPACE); goto doSimple; case 'w': state->result = NewRENode(state, REOP_ALNUM); goto doSimple; case 'W': state->result = NewRENode(state, REOP_NONALNUM); goto doSimple; /* IdentityEscape */ default: state->result = NewRENode(state, REOP_FLAT); if (!state->result) return JS_FALSE; state->result->u.flat.chr = c; state->result->u.flat.length = 1; state->result->kid = (void *) (state->cp - 1); state->progLength += 3; break; } break; case '[': state->result = NewRENode(state, REOP_CLASS); if (!state->result) return JS_FALSE; termStart = state->cp; state->result->u.ucclass.startIndex = termStart - state->cpbegin; for (;;) { if (state->cp == state->cpend) { js_ReportCompileErrorNumberUC(state->context, state->tokenStream, JSREPORT_TS | JSREPORT_ERROR, JSMSG_UNTERM_CLASS, termStart); return JS_FALSE; } if (*state->cp == '\\') { state->cp++; if (state->cp != state->cpend) state->cp++; continue; } if (*state->cp == ']') { state->result->u.ucclass.kidlen = state->cp - termStart; break; } state->cp++; } for (i = 0; i < CLASS_CACHE_SIZE; i++) { if (!state->classCache[i].start) { state->classCache[i].start = termStart; state->classCache[i].length = state->result->u.ucclass.kidlen; state->classCache[i].index = state->classCount; break; } if (state->classCache[i].length == state->result->u.ucclass.kidlen) { for (n = 0; ; n++) { if (n == state->classCache[i].length) { state->result->u.ucclass.index = state->classCache[i].index; goto claim; } if (state->classCache[i].start[n] != termStart[n]) break; } } } state->result->u.ucclass.index = state->classCount++; claim: /* * Call CalculateBitmapSize now as we want any errors it finds * to be reported during the parse phase, not at execution. */ if (!CalculateBitmapSize(state, state->result, termStart, state->cp++)) return JS_FALSE; /* * Update classBitmapsMem with number of bytes to hold bmsize bits, * which is (bitsCount + 7) / 8 or (highest_bit + 1 + 7) / 8 * or highest_bit / 8 + 1 where highest_bit is u.ucclass.bmsize. */ n = (state->result->u.ucclass.bmsize >> 3) + 1; if (n > CLASS_BITMAPS_MEM_LIMIT - state->classBitmapsMem) { js_ReportCompileErrorNumber(state->context, state->tokenStream, JSREPORT_TS | JSREPORT_ERROR, JSMSG_REGEXP_TOO_COMPLEX); return JS_FALSE; } state->classBitmapsMem += n; /* CLASS, */ state->progLength += 1 + GetCompactIndexWidth(state->result->u.ucclass.index); break; case '.': state->result = NewRENode(state, REOP_DOT); goto doSimple; case '{': { const jschar *errp = state->cp--; intN err; err = ParseMinMaxQuantifier(state, JS_TRUE); state->cp = errp; if (err < 0) goto asFlat; /* FALL THROUGH */ } case '*': case '+': case '?': js_ReportCompileErrorNumberUC(state->context, state->tokenStream, JSREPORT_TS | JSREPORT_ERROR, JSMSG_BAD_QUANTIFIER, state->cp - 1); return JS_FALSE; default: asFlat: state->result = NewRENode(state, REOP_FLAT); if (!state->result) return JS_FALSE; state->result->u.flat.chr = c; state->result->u.flat.length = 1; state->result->kid = (void *) (state->cp - 1); state->progLength += 3; break; } return ParseQuantifier(state); } static JSBool ParseQuantifier(CompilerState *state) { RENode *term; term = state->result; if (state->cp < state->cpend) { switch (*state->cp) { case '+': state->result = NewRENode(state, REOP_QUANT); if (!state->result) return JS_FALSE; state->result->u.range.min = 1; state->result->u.range.max = (uintN)-1; /* , ... */ state->progLength += 4; goto quantifier; case '*': state->result = NewRENode(state, REOP_QUANT); if (!state->result) return JS_FALSE; state->result->u.range.min = 0; state->result->u.range.max = (uintN)-1; /* , ... */ state->progLength += 4; goto quantifier; case '?': state->result = NewRENode(state, REOP_QUANT); if (!state->result) return JS_FALSE; state->result->u.range.min = 0; state->result->u.range.max = 1; /* , ... */ state->progLength += 4; goto quantifier; case '{': /* balance '}' */ { intN err; const jschar *errp = state->cp; err = ParseMinMaxQuantifier(state, JS_FALSE); if (err == 0) goto quantifier; if (err == -1) return JS_TRUE; js_ReportCompileErrorNumberUC(state->context, state->tokenStream, JSREPORT_TS | JSREPORT_ERROR, err, errp); return JS_FALSE; } default:; } } return JS_TRUE; quantifier: if (state->treeDepth == TREE_DEPTH_MAX) { js_ReportCompileErrorNumber(state->context, state->tokenStream, JSREPORT_TS | JSREPORT_ERROR, JSMSG_REGEXP_TOO_COMPLEX); return JS_FALSE; } ++state->treeDepth; ++state->cp; state->result->kid = term; if (state->cp < state->cpend && *state->cp == '?') { ++state->cp; state->result->u.range.greedy = JS_FALSE; } else { state->result->u.range.greedy = JS_TRUE; } return JS_TRUE; } static intN ParseMinMaxQuantifier(CompilerState *state, JSBool ignoreValues) { uintN min, max; jschar c; const jschar *errp = state->cp++; c = *state->cp; if (JS7_ISDEC(c)) { ++state->cp; min = GetDecimalValue(c, 0xFFFF, NULL, state); c = *state->cp; if (!ignoreValues && min == OVERFLOW_VALUE) return JSMSG_MIN_TOO_BIG; if (c == ',') { c = *++state->cp; if (JS7_ISDEC(c)) { ++state->cp; max = GetDecimalValue(c, 0xFFFF, NULL, state); c = *state->cp; if (!ignoreValues && max == OVERFLOW_VALUE) return JSMSG_MAX_TOO_BIG; if (!ignoreValues && min > max) return JSMSG_OUT_OF_ORDER; } else { max = (uintN)-1; } } else { max = min; } if (c == '}') { state->result = NewRENode(state, REOP_QUANT); if (!state->result) return JS_FALSE; state->result->u.range.min = min; state->result->u.range.max = max; /* * QUANT, , , ... * where is written as compact(max+1) to make * (uintN)-1 sentinel to occupy 1 byte, not width_of(max)+1. */ state->progLength += (1 + GetCompactIndexWidth(min) + GetCompactIndexWidth(max + 1) +3); return 0; } } state->cp = errp; return -1; } static JSBool SetForwardJumpOffset(jsbytecode *jump, jsbytecode *target) { ptrdiff_t offset = target - jump; /* Check that target really points forward. */ JS_ASSERT(offset >= 2); if ((size_t)offset > OFFSET_MAX) return JS_FALSE; jump[0] = JUMP_OFFSET_HI(offset); jump[1] = JUMP_OFFSET_LO(offset); return JS_TRUE; } /* * Generate bytecode for the tree rooted at t using an explicit stack instead * of recursion. */ static jsbytecode * EmitREBytecode(CompilerState *state, JSRegExp *re, size_t treeDepth, jsbytecode *pc, RENode *t) { EmitStateStackEntry *emitStateSP, *emitStateStack; RECharSet *charSet; REOp op; if (treeDepth == 0) { emitStateStack = NULL; } else { emitStateStack = (EmitStateStackEntry *)JS_malloc(state->context, sizeof(EmitStateStackEntry) * treeDepth); if (!emitStateStack) return NULL; } emitStateSP = emitStateStack; op = t->op; for (;;) { *pc++ = op; switch (op) { case REOP_EMPTY: --pc; break; case REOP_ALTPREREQ2: case REOP_ALTPREREQ: JS_ASSERT(emitStateSP); emitStateSP->altHead = pc - 1; emitStateSP->endTermFixup = pc; pc += OFFSET_LEN; SET_ARG(pc, t->u.altprereq.ch1); pc += ARG_LEN; SET_ARG(pc, t->u.altprereq.ch2); pc += ARG_LEN; emitStateSP->nextAltFixup = pc; /* offset to next alternate */ pc += OFFSET_LEN; emitStateSP->continueNode = t; emitStateSP->continueOp = REOP_JUMP; emitStateSP->jumpToJumpFlag = JS_FALSE; ++emitStateSP; JS_ASSERT((size_t)(emitStateSP - emitStateStack) <= treeDepth); t = (RENode *) t->kid; op = t->op; continue; case REOP_JUMP: emitStateSP->nextTermFixup = pc; /* offset to following term */ pc += OFFSET_LEN; if (!SetForwardJumpOffset(emitStateSP->nextAltFixup, pc)) goto jump_too_big; emitStateSP->continueOp = REOP_ENDALT; ++emitStateSP; JS_ASSERT((size_t)(emitStateSP - emitStateStack) <= treeDepth); t = t->u.kid2; op = t->op; continue; case REOP_ENDALT: /* * If we already patched emitStateSP->nextTermFixup to jump to * a nearer jump, to avoid 16-bit immediate offset overflow, we * are done here. */ if (emitStateSP->jumpToJumpFlag) break; /* * Fix up the REOP_JUMP offset to go to the op after REOP_ENDALT. * REOP_ENDALT is executed only on successful match of the last * alternate in a group. */ if (!SetForwardJumpOffset(emitStateSP->nextTermFixup, pc)) goto jump_too_big; if (t->op != REOP_ALT) { if (!SetForwardJumpOffset(emitStateSP->endTermFixup, pc)) goto jump_too_big; } /* * If the program is bigger than the REOP_JUMP offset range, then * we must check for alternates before this one that are part of * the same group, and fix up their jump offsets to target jumps * close enough to fit in a 16-bit unsigned offset immediate. */ if ((size_t)(pc - re->program) > OFFSET_MAX && emitStateSP > emitStateStack) { EmitStateStackEntry *esp, *esp2; jsbytecode *alt, *jump; ptrdiff_t span, header; esp2 = emitStateSP; alt = esp2->altHead; for (esp = esp2 - 1; esp >= emitStateStack; --esp) { if (esp->continueOp == REOP_ENDALT && !esp->jumpToJumpFlag && esp->nextTermFixup + OFFSET_LEN == alt && (size_t)(pc - ((esp->continueNode->op != REOP_ALT) ? esp->endTermFixup : esp->nextTermFixup)) > OFFSET_MAX) { alt = esp->altHead; jump = esp->nextTermFixup; /* * The span must be 1 less than the distance from * jump offset to jump offset, so we actually jump * to a REOP_JUMP bytecode, not to its offset! */ for (;;) { JS_ASSERT(jump < esp2->nextTermFixup); span = esp2->nextTermFixup - jump - 1; if ((size_t)span <= OFFSET_MAX) break; do { if (--esp2 == esp) goto jump_too_big; } while (esp2->continueOp != REOP_ENDALT); } jump[0] = JUMP_OFFSET_HI(span); jump[1] = JUMP_OFFSET_LO(span); if (esp->continueNode->op != REOP_ALT) { /* * We must patch the offset at esp->endTermFixup * as well, for the REOP_ALTPREREQ{,2} opcodes. * If we're unlucky and endTermFixup is more than * OFFSET_MAX bytes from its target, we cheat by * jumping 6 bytes to the jump whose offset is at * esp->nextTermFixup, which has the same target. */ jump = esp->endTermFixup; header = esp->nextTermFixup - jump; span += header; if ((size_t)span > OFFSET_MAX) span = header; jump[0] = JUMP_OFFSET_HI(span); jump[1] = JUMP_OFFSET_LO(span); } esp->jumpToJumpFlag = JS_TRUE; } } } break; case REOP_ALT: JS_ASSERT(emitStateSP); emitStateSP->altHead = pc - 1; emitStateSP->nextAltFixup = pc; /* offset to next alternate */ pc += OFFSET_LEN; emitStateSP->continueNode = t; emitStateSP->continueOp = REOP_JUMP; emitStateSP->jumpToJumpFlag = JS_FALSE; ++emitStateSP; JS_ASSERT((size_t)(emitStateSP - emitStateStack) <= treeDepth); t = t->kid; op = t->op; continue; case REOP_FLAT: /* * Coalesce FLATs if possible and if it would not increase bytecode * beyond preallocated limit. The latter happens only when bytecode * size for coalesced string with offset p and length 2 exceeds 6 * bytes preallocated for 2 single char nodes, i.e. when * 1 + GetCompactIndexWidth(p) + GetCompactIndexWidth(2) > 6 or * GetCompactIndexWidth(p) > 4. * Since when GetCompactIndexWidth(p) <= 4 coalescing of 3 or more * nodes strictly decreases bytecode size, the check has to be * done only for the first coalescing. */ if (t->kid && GetCompactIndexWidth((jschar *)t->kid - state->cpbegin) <= 4) { while (t->next && t->next->op == REOP_FLAT && (jschar*)t->kid + t->u.flat.length == (jschar*)t->next->kid) { t->u.flat.length += t->next->u.flat.length; t->next = t->next->next; } } if (t->kid && t->u.flat.length > 1) { pc[-1] = (state->flags & JSREG_FOLD) ? REOP_FLATi : REOP_FLAT; pc = WriteCompactIndex(pc, (jschar *)t->kid - state->cpbegin); pc = WriteCompactIndex(pc, t->u.flat.length); } else if (t->u.flat.chr < 256) { pc[-1] = (state->flags & JSREG_FOLD) ? REOP_FLAT1i : REOP_FLAT1; *pc++ = (jsbytecode) t->u.flat.chr; } else { pc[-1] = (state->flags & JSREG_FOLD) ? REOP_UCFLAT1i : REOP_UCFLAT1; SET_ARG(pc, t->u.flat.chr); pc += ARG_LEN; } break; case REOP_LPAREN: JS_ASSERT(emitStateSP); pc = WriteCompactIndex(pc, t->u.parenIndex); emitStateSP->continueNode = t; emitStateSP->continueOp = REOP_RPAREN; ++emitStateSP; JS_ASSERT((size_t)(emitStateSP - emitStateStack) <= treeDepth); t = (RENode *) t->kid; op = t->op; continue; case REOP_RPAREN: pc = WriteCompactIndex(pc, t->u.parenIndex); break; case REOP_BACKREF: pc = WriteCompactIndex(pc, t->u.parenIndex); break; case REOP_ASSERT: JS_ASSERT(emitStateSP); emitStateSP->nextTermFixup = pc; pc += OFFSET_LEN; emitStateSP->continueNode = t; emitStateSP->continueOp = REOP_ASSERTTEST; ++emitStateSP; JS_ASSERT((size_t)(emitStateSP - emitStateStack) <= treeDepth); t = (RENode *) t->kid; op = t->op; continue; case REOP_ASSERTTEST: case REOP_ASSERTNOTTEST: if (!SetForwardJumpOffset(emitStateSP->nextTermFixup, pc)) goto jump_too_big; break; case REOP_ASSERT_NOT: JS_ASSERT(emitStateSP); emitStateSP->nextTermFixup = pc; pc += OFFSET_LEN; emitStateSP->continueNode = t; emitStateSP->continueOp = REOP_ASSERTNOTTEST; ++emitStateSP; JS_ASSERT((size_t)(emitStateSP - emitStateStack) <= treeDepth); t = (RENode *) t->kid; op = t->op; continue; case REOP_QUANT: JS_ASSERT(emitStateSP); if (t->u.range.min == 0 && t->u.range.max == (uintN)-1) { pc[-1] = (t->u.range.greedy) ? REOP_STAR : REOP_MINIMALSTAR; } else if (t->u.range.min == 0 && t->u.range.max == 1) { pc[-1] = (t->u.range.greedy) ? REOP_OPT : REOP_MINIMALOPT; } else if (t->u.range.min == 1 && t->u.range.max == (uintN) -1) { pc[-1] = (t->u.range.greedy) ? REOP_PLUS : REOP_MINIMALPLUS; } else { if (!t->u.range.greedy) pc[-1] = REOP_MINIMALQUANT; pc = WriteCompactIndex(pc, t->u.range.min); /* * Write max + 1 to avoid using size_t(max) + 1 bytes * for (uintN)-1 sentinel. */ pc = WriteCompactIndex(pc, t->u.range.max + 1); } emitStateSP->nextTermFixup = pc; pc += OFFSET_LEN; emitStateSP->continueNode = t; emitStateSP->continueOp = REOP_ENDCHILD; ++emitStateSP; JS_ASSERT((size_t)(emitStateSP - emitStateStack) <= treeDepth); t = (RENode *) t->kid; op = t->op; continue; case REOP_ENDCHILD: if (!SetForwardJumpOffset(emitStateSP->nextTermFixup, pc)) goto jump_too_big; break; case REOP_CLASS: if (!t->u.ucclass.sense) pc[-1] = REOP_NCLASS; pc = WriteCompactIndex(pc, t->u.ucclass.index); charSet = &re->classList[t->u.ucclass.index]; charSet->converted = JS_FALSE; charSet->length = t->u.ucclass.bmsize; charSet->u.src.startIndex = t->u.ucclass.startIndex; charSet->u.src.length = t->u.ucclass.kidlen; charSet->sense = t->u.ucclass.sense; break; default: break; } t = t->next; if (t) { op = t->op; } else { if (emitStateSP == emitStateStack) break; --emitStateSP; t = emitStateSP->continueNode; op = emitStateSP->continueOp; } } cleanup: if (emitStateStack) JS_free(state->context, emitStateStack); return pc; jump_too_big: js_ReportCompileErrorNumber(state->context, state->tokenStream, JSREPORT_TS | JSREPORT_ERROR, JSMSG_REGEXP_TOO_COMPLEX); pc = NULL; goto cleanup; } JSRegExp * js_NewRegExp(JSContext *cx, JSTokenStream *ts, JSString *str, uintN flags, JSBool flat) { JSRegExp *re; void *mark; CompilerState state; size_t resize; jsbytecode *endPC; uintN i; size_t len; re = NULL; mark = JS_ARENA_MARK(&cx->tempPool); len = JSSTRING_LENGTH(str); state.context = cx; state.tokenStream = ts; state.cp = js_UndependString(cx, str); if (!state.cp) goto out; state.cpbegin = state.cp; state.cpend = state.cp + len; state.flags = flags; state.parenCount = 0; state.classCount = 0; state.progLength = 0; state.treeDepth = 0; state.classBitmapsMem = 0; for (i = 0; i < CLASS_CACHE_SIZE; i++) state.classCache[i].start = NULL; if (len != 0 && flat) { state.result = NewRENode(&state, REOP_FLAT); state.result->u.flat.chr = *state.cpbegin; state.result->u.flat.length = len; state.result->kid = (void *) state.cpbegin; /* Flat bytecode: REOP_FLAT compact(string_offset) compact(len). */ state.progLength += 1 + GetCompactIndexWidth(0) + GetCompactIndexWidth(len); } else { if (!ParseRegExp(&state)) goto out; } resize = offsetof(JSRegExp, program) + state.progLength + 1; re = (JSRegExp *) JS_malloc(cx, resize); if (!re) goto out; re->nrefs = 1; JS_ASSERT(state.classBitmapsMem <= CLASS_BITMAPS_MEM_LIMIT); re->classCount = state.classCount; if (re->classCount) { re->classList = (RECharSet *) JS_malloc(cx, re->classCount * sizeof(RECharSet)); if (!re->classList) { js_DestroyRegExp(cx, re); re = NULL; goto out; } for (i = 0; i < re->classCount; i++) re->classList[i].converted = JS_FALSE; } else { re->classList = NULL; } endPC = EmitREBytecode(&state, re, state.treeDepth, re->program, state.result); if (!endPC) { js_DestroyRegExp(cx, re); re = NULL; goto out; } *endPC++ = REOP_END; /* * Check whether size was overestimated and shrink using realloc. * This is safe since no pointers to newly parsed regexp or its parts * besides re exist here. */ if ((size_t)(endPC - re->program) != state.progLength + 1) { JSRegExp *tmp; JS_ASSERT((size_t)(endPC - re->program) < state.progLength + 1); resize = offsetof(JSRegExp, program) + (endPC - re->program); tmp = (JSRegExp *) JS_realloc(cx, re, resize); if (tmp) re = tmp; } re->flags = flags; re->cloneIndex = 0; re->parenCount = state.parenCount; re->source = str; out: JS_ARENA_RELEASE(&cx->tempPool, mark); return re; } JSRegExp * js_NewRegExpOpt(JSContext *cx, JSTokenStream *ts, JSString *str, JSString *opt, JSBool flat) { uintN flags; jschar *s; size_t i, n; char charBuf[2]; flags = 0; if (opt) { s = JSSTRING_CHARS(opt); for (i = 0, n = JSSTRING_LENGTH(opt); i < n; i++) { switch (s[i]) { case 'g': flags |= JSREG_GLOB; break; case 'i': flags |= JSREG_FOLD; break; case 'm': flags |= JSREG_MULTILINE; break; default: charBuf[0] = (char)s[i]; charBuf[1] = '\0'; js_ReportCompileErrorNumber(cx, ts, JSREPORT_TS | JSREPORT_ERROR, JSMSG_BAD_FLAG, charBuf); return NULL; } } } return js_NewRegExp(cx, ts, str, flags, flat); } /* * Save the current state of the match - the position in the input * text as well as the position in the bytecode. The state of any * parent expressions is also saved (preceding state). * Contents of parenCount parentheses from parenIndex are also saved. */ static REBackTrackData * PushBackTrackState(REGlobalData *gData, REOp op, jsbytecode *target, REMatchState *x, const jschar *cp, size_t parenIndex, size_t parenCount) { size_t i; REBackTrackData *result = (REBackTrackData *) ((char *)gData->backTrackSP + gData->cursz); size_t sz = sizeof(REBackTrackData) + gData->stateStackTop * sizeof(REProgState) + parenCount * sizeof(RECapture); ptrdiff_t btsize = gData->backTrackStackSize; ptrdiff_t btincr = ((char *)result + sz) - ((char *)gData->backTrackStack + btsize); if (btincr > 0) { ptrdiff_t offset = (char *)result - (char *)gData->backTrackStack; btincr = JS_ROUNDUP(btincr, btsize); JS_ARENA_GROW_CAST(gData->backTrackStack, REBackTrackData *, &gData->pool, btsize, btincr); if (!gData->backTrackStack) { JS_ReportOutOfMemory(gData->cx); gData->ok = JS_FALSE; return NULL; } gData->backTrackStackSize = btsize + btincr; result = (REBackTrackData *) ((char *)gData->backTrackStack + offset); } gData->backTrackSP = result; result->sz = gData->cursz; gData->cursz = sz; result->backtrack_op = op; result->backtrack_pc = target; result->cp = cp; result->parenCount = parenCount; result->saveStateStackTop = gData->stateStackTop; JS_ASSERT(gData->stateStackTop); memcpy(result + 1, gData->stateStack, sizeof(REProgState) * result->saveStateStackTop); if (parenCount != 0) { result->parenIndex = parenIndex; memcpy((char *)(result + 1) + sizeof(REProgState) * result->saveStateStackTop, &x->parens[parenIndex], sizeof(RECapture) * parenCount); for (i = 0; i != parenCount; i++) x->parens[parenIndex + i].index = -1; } return result; } /* * Consecutive literal characters. */ #if 0 static REMatchState * FlatNMatcher(REGlobalData *gData, REMatchState *x, jschar *matchChars, size_t length) { size_t i; if (length > gData->cpend - x->cp) return NULL; for (i = 0; i != length; i++) { if (matchChars[i] != x->cp[i]) return NULL; } x->cp += length; return x; } #endif static REMatchState * FlatNIMatcher(REGlobalData *gData, REMatchState *x, jschar *matchChars, size_t length) { size_t i; JS_ASSERT(gData->cpend >= x->cp); if (length > (size_t)(gData->cpend - x->cp)) return NULL; for (i = 0; i != length; i++) { if (upcase(matchChars[i]) != upcase(x->cp[i])) return NULL; } x->cp += length; return x; } /* * 1. Evaluate DecimalEscape to obtain an EscapeValue E. * 2. If E is not a character then go to step 6. * 3. Let ch be E's character. * 4. Let A be a one-element RECharSet containing the character ch. * 5. Call CharacterSetMatcher(A, false) and return its Matcher result. * 6. E must be an integer. Let n be that integer. * 7. If n=0 or n>NCapturingParens then throw a SyntaxError exception. * 8. Return an internal Matcher closure that takes two arguments, a State x * and a Continuation c, and performs the following: * 1. Let cap be x's captures internal array. * 2. Let s be cap[n]. * 3. If s is undefined, then call c(x) and return its result. * 4. Let e be x's endIndex. * 5. Let len be s's length. * 6. Let f be e+len. * 7. If f>InputLength, return failure. * 8. If there exists an integer i between 0 (inclusive) and len (exclusive) * such that Canonicalize(s[i]) is not the same character as * Canonicalize(Input [e+i]), then return failure. * 9. Let y be the State (f, cap). * 10. Call c(y) and return its result. */ static REMatchState * BackrefMatcher(REGlobalData *gData, REMatchState *x, size_t parenIndex) { size_t len, i; const jschar *parenContent; RECapture *cap = &x->parens[parenIndex]; if (cap->index == -1) return x; len = cap->length; if (x->cp + len > gData->cpend) return NULL; parenContent = &gData->cpbegin[cap->index]; if (gData->regexp->flags & JSREG_FOLD) { for (i = 0; i < len; i++) { if (upcase(parenContent[i]) != upcase(x->cp[i])) return NULL; } } else { for (i = 0; i < len; i++) { if (parenContent[i] != x->cp[i]) return NULL; } } x->cp += len; return x; } /* Add a single character to the RECharSet */ static void AddCharacterToCharSet(RECharSet *cs, jschar c) { uintN byteIndex = (uintN)(c >> 3); JS_ASSERT(c <= cs->length); cs->u.bits[byteIndex] |= 1 << (c & 0x7); } /* Add a character range, c1 to c2 (inclusive) to the RECharSet */ static void AddCharacterRangeToCharSet(RECharSet *cs, jschar c1, jschar c2) { uintN i; uintN byteIndex1 = (uintN)(c1 >> 3); uintN byteIndex2 = (uintN)(c2 >> 3); JS_ASSERT((c2 <= cs->length) && (c1 <= c2)); c1 &= 0x7; c2 &= 0x7; if (byteIndex1 == byteIndex2) { cs->u.bits[byteIndex1] |= ((uint8)0xFF >> (7 - (c2 - c1))) << c1; } else { cs->u.bits[byteIndex1] |= 0xFF << c1; for (i = byteIndex1 + 1; i < byteIndex2; i++) cs->u.bits[i] = 0xFF; cs->u.bits[byteIndex2] |= (uint8)0xFF >> (7 - c2); } } /* Compile the source of the class into a RECharSet */ static JSBool ProcessCharSet(REGlobalData *gData, RECharSet *charSet) { const jschar *src, *end; JSBool inRange = JS_FALSE; jschar rangeStart = 0; uintN byteLength, n; jschar c, thisCh; intN nDigits, i; JS_ASSERT(!charSet->converted); /* * Assert that startIndex and length points to chars inside [] inside * source string. */ JS_ASSERT(1 <= charSet->u.src.startIndex); JS_ASSERT(charSet->u.src.startIndex < JSSTRING_LENGTH(gData->regexp->source)); JS_ASSERT(charSet->u.src.length <= JSSTRING_LENGTH(gData->regexp->source) - 1 - charSet->u.src.startIndex); charSet->converted = JS_TRUE; src = JSSTRING_CHARS(gData->regexp->source) + charSet->u.src.startIndex; end = src + charSet->u.src.length; JS_ASSERT(src[-1] == '['); JS_ASSERT(end[0] == ']'); byteLength = (charSet->length >> 3) + 1; charSet->u.bits = (uint8 *)JS_malloc(gData->cx, byteLength); if (!charSet->u.bits) { JS_ReportOutOfMemory(gData->cx); gData->ok = JS_FALSE; return JS_FALSE; } memset(charSet->u.bits, 0, byteLength); if (src == end) return JS_TRUE; if (*src == '^') { JS_ASSERT(charSet->sense == JS_FALSE); ++src; } else { JS_ASSERT(charSet->sense == JS_TRUE); } while (src != end) { switch (*src) { case '\\': ++src; c = *src++; switch (c) { case 'b': thisCh = 0x8; break; case 'f': thisCh = 0xC; break; case 'n': thisCh = 0xA; break; case 'r': thisCh = 0xD; break; case 't': thisCh = 0x9; break; case 'v': thisCh = 0xB; break; case 'c': if (src < end && JS_ISWORD(*src)) { thisCh = (jschar)(*src++ & 0x1F); } else { --src; thisCh = '\\'; } break; case 'x': nDigits = 2; goto lexHex; case 'u': nDigits = 4; lexHex: n = 0; for (i = 0; (i < nDigits) && (src < end); i++) { uintN digit; c = *src++; if (!isASCIIHexDigit(c, &digit)) { /* * Back off to accepting the original '\' * as a literal */ src -= i + 1; n = '\\'; break; } n = (n << 4) | digit; } thisCh = (jschar)n; break; case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': /* * This is a non-ECMA extension - decimal escapes (in this * case, octal!) are supposed to be an error inside class * ranges, but supported here for backwards compatibility. */ n = JS7_UNDEC(c); c = *src; if ('0' <= c && c <= '7') { src++; n = 8 * n + JS7_UNDEC(c); c = *src; if ('0' <= c && c <= '7') { src++; i = 8 * n + JS7_UNDEC(c); if (i <= 0377) n = i; else src--; } } thisCh = (jschar)n; break; case 'd': AddCharacterRangeToCharSet(charSet, '0', '9'); continue; /* don't need range processing */ case 'D': AddCharacterRangeToCharSet(charSet, 0, '0' - 1); AddCharacterRangeToCharSet(charSet, (jschar)('9' + 1), (jschar)charSet->length); continue; case 's': for (i = (intN)charSet->length; i >= 0; i--) if (JS_ISSPACE(i)) AddCharacterToCharSet(charSet, (jschar)i); continue; case 'S': for (i = (intN)charSet->length; i >= 0; i--) if (!JS_ISSPACE(i)) AddCharacterToCharSet(charSet, (jschar)i); continue; case 'w': for (i = (intN)charSet->length; i >= 0; i--) if (JS_ISWORD(i)) AddCharacterToCharSet(charSet, (jschar)i); continue; case 'W': for (i = (intN)charSet->length; i >= 0; i--) if (!JS_ISWORD(i)) AddCharacterToCharSet(charSet, (jschar)i); continue; default: thisCh = c; break; } break; default: thisCh = *src++; break; } if (inRange) { if (gData->regexp->flags & JSREG_FOLD) { AddCharacterRangeToCharSet(charSet, upcase(rangeStart), upcase(thisCh)); AddCharacterRangeToCharSet(charSet, downcase(rangeStart), downcase(thisCh)); } else { AddCharacterRangeToCharSet(charSet, rangeStart, thisCh); } inRange = JS_FALSE; } else { if (gData->regexp->flags & JSREG_FOLD) { AddCharacterToCharSet(charSet, upcase(thisCh)); AddCharacterToCharSet(charSet, downcase(thisCh)); } else { AddCharacterToCharSet(charSet, thisCh); } if (src < end - 1) { if (*src == '-') { ++src; inRange = JS_TRUE; rangeStart = thisCh; } } } } return JS_TRUE; } void js_DestroyRegExp(JSContext *cx, JSRegExp *re) { if (JS_ATOMIC_DECREMENT(&re->nrefs) == 0) { if (re->classList) { uintN i; for (i = 0; i < re->classCount; i++) { if (re->classList[i].converted) JS_free(cx, re->classList[i].u.bits); re->classList[i].u.bits = NULL; } JS_free(cx, re->classList); } JS_free(cx, re); } } static JSBool ReallocStateStack(REGlobalData *gData) { size_t limit = gData->stateStackLimit; size_t sz = sizeof(REProgState) * limit; JS_ARENA_GROW_CAST(gData->stateStack, REProgState *, &gData->pool, sz, sz); if (!gData->stateStack) { gData->ok = JS_FALSE; return JS_FALSE; } gData->stateStackLimit = limit + limit; return JS_TRUE; } #define PUSH_STATE_STACK(data) \ JS_BEGIN_MACRO \ ++(data)->stateStackTop; \ if ((data)->stateStackTop == (data)->stateStackLimit && \ !ReallocStateStack((data))) { \ return NULL; \ } \ JS_END_MACRO /* * Apply the current op against the given input to see if it's going to match * or fail. Return false if we don't get a match, true if we do. If updatecp is * true, then update the current state's cp. Always update startpc to the next * op. */ static REMatchState * SimpleMatch(REGlobalData *gData, REMatchState *x, REOp op, jsbytecode **startpc, JSBool updatecp) { REMatchState *result = NULL; jschar matchCh; size_t parenIndex; size_t offset, length, index; jsbytecode *pc = *startpc; /* pc has already been incremented past op */ jschar *source; const jschar *startcp = x->cp; jschar ch; RECharSet *charSet; switch (op) { case REOP_BOL: if (x->cp != gData->cpbegin) { if (!gData->cx->regExpStatics.multiline && !(gData->regexp->flags & JSREG_MULTILINE)) { break; } if (!RE_IS_LINE_TERM(x->cp[-1])) break; } result = x; break; case REOP_EOL: if (x->cp != gData->cpend) { if (!gData->cx->regExpStatics.multiline && !(gData->regexp->flags & JSREG_MULTILINE)) { break; } if (!RE_IS_LINE_TERM(*x->cp)) break; } result = x; break; case REOP_WBDRY: if ((x->cp == gData->cpbegin || !JS_ISWORD(x->cp[-1])) ^ !(x->cp != gData->cpend && JS_ISWORD(*x->cp))) { result = x; } break; case REOP_WNONBDRY: if ((x->cp == gData->cpbegin || !JS_ISWORD(x->cp[-1])) ^ (x->cp != gData->cpend && JS_ISWORD(*x->cp))) { result = x; } break; case REOP_DOT: if (x->cp != gData->cpend && !RE_IS_LINE_TERM(*x->cp)) { result = x; result->cp++; } break; case REOP_DIGIT: if (x->cp != gData->cpend && JS_ISDIGIT(*x->cp)) { result = x; result->cp++; } break; case REOP_NONDIGIT: if (x->cp != gData->cpend && !JS_ISDIGIT(*x->cp)) { result = x; result->cp++; } break; case REOP_ALNUM: if (x->cp != gData->cpend && JS_ISWORD(*x->cp)) { result = x; result->cp++; } break; case REOP_NONALNUM: if (x->cp != gData->cpend && !JS_ISWORD(*x->cp)) { result = x; result->cp++; } break; case REOP_SPACE: if (x->cp != gData->cpend && JS_ISSPACE(*x->cp)) { result = x; result->cp++; } break; case REOP_NONSPACE: if (x->cp != gData->cpend && !JS_ISSPACE(*x->cp)) { result = x; result->cp++; } break; case REOP_BACKREF: pc = ReadCompactIndex(pc, &parenIndex); JS_ASSERT(parenIndex < gData->regexp->parenCount); result = BackrefMatcher(gData, x, parenIndex); break; case REOP_FLAT: pc = ReadCompactIndex(pc, &offset); JS_ASSERT(offset < JSSTRING_LENGTH(gData->regexp->source)); pc = ReadCompactIndex(pc, &length); JS_ASSERT(1 <= length); JS_ASSERT(length <= JSSTRING_LENGTH(gData->regexp->source) - offset); if (length <= (size_t)(gData->cpend - x->cp)) { source = JSSTRING_CHARS(gData->regexp->source) + offset; for (index = 0; index != length; index++) { if (source[index] != x->cp[index]) return NULL; } x->cp += length; result = x; } break; case REOP_FLAT1: matchCh = *pc++; if (x->cp != gData->cpend && *x->cp == matchCh) { result = x; result->cp++; } break; case REOP_FLATi: pc = ReadCompactIndex(pc, &offset); JS_ASSERT(offset < JSSTRING_LENGTH(gData->regexp->source)); pc = ReadCompactIndex(pc, &length); JS_ASSERT(1 <= length); JS_ASSERT(length <= JSSTRING_LENGTH(gData->regexp->source) - offset); source = JSSTRING_CHARS(gData->regexp->source); result = FlatNIMatcher(gData, x, source + offset, length); break; case REOP_FLAT1i: matchCh = *pc++; if (x->cp != gData->cpend && upcase(*x->cp) == upcase(matchCh)) { result = x; result->cp++; } break; case REOP_UCFLAT1: matchCh = GET_ARG(pc); pc += ARG_LEN; if (x->cp != gData->cpend && *x->cp == matchCh) { result = x; result->cp++; } break; case REOP_UCFLAT1i: matchCh = GET_ARG(pc); pc += ARG_LEN; if (x->cp != gData->cpend && upcase(*x->cp) == upcase(matchCh)) { result = x; result->cp++; } break; case REOP_CLASS: pc = ReadCompactIndex(pc, &index); JS_ASSERT(index < gData->regexp->classCount); if (x->cp != gData->cpend) { charSet = &gData->regexp->classList[index]; JS_ASSERT(charSet->converted); ch = *x->cp; index = ch >> 3; if (charSet->length != 0 && ch <= charSet->length && (charSet->u.bits[index] & (1 << (ch & 0x7)))) { result = x; result->cp++; } } break; case REOP_NCLASS: pc = ReadCompactIndex(pc, &index); JS_ASSERT(index < gData->regexp->classCount); if (x->cp != gData->cpend) { charSet = &gData->regexp->classList[index]; JS_ASSERT(charSet->converted); ch = *x->cp; index = ch >> 3; if (charSet->length == 0 || ch > charSet->length || !(charSet->u.bits[index] & (1 << (ch & 0x7)))) { result = x; result->cp++; } } break; default: JS_ASSERT(JS_FALSE); } if (result) { if (!updatecp) x->cp = startcp; *startpc = pc; return result; } x->cp = startcp; return NULL; } static REMatchState * ExecuteREBytecode(REGlobalData *gData, REMatchState *x) { REMatchState *result = NULL; REBackTrackData *backTrackData; jsbytecode *nextpc, *testpc; REOp nextop; RECapture *cap; REProgState *curState; const jschar *startcp; size_t parenIndex, k; size_t parenSoFar = 0; jschar matchCh1, matchCh2; RECharSet *charSet; JSBranchCallback onbranch = gData->cx->branchCallback; uintN onbranchCalls = 0; #define ONBRANCH_CALLS_MASK 127 #define CHECK_BRANCH() \ JS_BEGIN_MACRO \ if (onbranch && \ (++onbranchCalls & ONBRANCH_CALLS_MASK) == 0 && \ !(*onbranch)(gData->cx, NULL)) { \ gData->ok = JS_FALSE; \ return NULL; \ } \ JS_END_MACRO JSBool anchor; jsbytecode *pc = gData->regexp->program; REOp op = (REOp) *pc++; /* * If the first node is a simple match, step the index into the string * until that match is made, or fail if it can't be found at all. */ if (REOP_IS_SIMPLE(op)) { anchor = JS_FALSE; while (x->cp <= gData->cpend) { nextpc = pc; /* reset back to start each time */ result = SimpleMatch(gData, x, op, &nextpc, JS_TRUE); if (result) { anchor = JS_TRUE; x = result; pc = nextpc; /* accept skip to next opcode */ op = (REOp) *pc++; break; } gData->skipped++; x->cp++; } if (!anchor) return NULL; } for (;;) { if (REOP_IS_SIMPLE(op)) { result = SimpleMatch(gData, x, op, &pc, JS_TRUE); } else { curState = &gData->stateStack[gData->stateStackTop]; switch (op) { case REOP_EMPTY: result = x; break; case REOP_ALTPREREQ2: nextpc = pc + GET_OFFSET(pc); /* start of next op */ pc += ARG_LEN; matchCh2 = GET_ARG(pc); pc += ARG_LEN; k = GET_ARG(pc); pc += ARG_LEN; if (x->cp != gData->cpend) { if (*x->cp == matchCh2) goto doAlt; charSet = &gData->regexp->classList[k]; if (!charSet->converted && !ProcessCharSet(gData, charSet)) return NULL; matchCh1 = *x->cp; k = matchCh1 >> 3; if ((charSet->length == 0 || matchCh1 > charSet->length || !(charSet->u.bits[k] & (1 << (matchCh1 & 0x7)))) ^ charSet->sense) { goto doAlt; } } result = NULL; break; case REOP_ALTPREREQ: nextpc = pc + GET_OFFSET(pc); /* start of next op */ pc += ARG_LEN; matchCh1 = GET_ARG(pc); pc += ARG_LEN; matchCh2 = GET_ARG(pc); pc += ARG_LEN; if (x->cp == gData->cpend || (*x->cp != matchCh1 && *x->cp != matchCh2)) { result = NULL; break; } /* else false thru... */ case REOP_ALT: doAlt: nextpc = pc + GET_OFFSET(pc); /* start of next alternate */ pc += ARG_LEN; /* start of this alternate */ curState->parenSoFar = parenSoFar; PUSH_STATE_STACK(gData); op = (REOp) *pc++; startcp = x->cp; if (REOP_IS_SIMPLE(op)) { if (!SimpleMatch(gData, x, op, &pc, JS_TRUE)) { op = (REOp) *nextpc++; pc = nextpc; continue; } result = x; op = (REOp) *pc++; } nextop = (REOp) *nextpc++; if (!PushBackTrackState(gData, nextop, nextpc, x, startcp, 0, 0)) return NULL; continue; /* * Occurs at (successful) end of REOP_ALT, */ case REOP_JUMP: --gData->stateStackTop; pc += GET_OFFSET(pc); op = (REOp) *pc++; continue; /* * Occurs at last (successful) end of REOP_ALT, */ case REOP_ENDALT: --gData->stateStackTop; op = (REOp) *pc++; continue; case REOP_LPAREN: pc = ReadCompactIndex(pc, &parenIndex); JS_ASSERT(parenIndex < gData->regexp->parenCount); if (parenIndex + 1 > parenSoFar) parenSoFar = parenIndex + 1; x->parens[parenIndex].index = x->cp - gData->cpbegin; x->parens[parenIndex].length = 0; op = (REOp) *pc++; continue; case REOP_RPAREN: pc = ReadCompactIndex(pc, &parenIndex); JS_ASSERT(parenIndex < gData->regexp->parenCount); cap = &x->parens[parenIndex]; /* * FIXME: https://bugzilla.mozilla.org/show_bug.cgi?id=346090 * This wallpaper prevents a case where we somehow took a step * backward in input while minimally-matching an empty string. */ if (x->cp < gData->cpbegin + cap->index) cap->index = -1; cap->length = x->cp - (gData->cpbegin + cap->index); op = (REOp) *pc++; continue; case REOP_ASSERT: nextpc = pc + GET_OFFSET(pc); /* start of term after ASSERT */ pc += ARG_LEN; /* start of ASSERT child */ op = (REOp) *pc++; testpc = pc; if (REOP_IS_SIMPLE(op) && !SimpleMatch(gData, x, op, &testpc, JS_FALSE)) { result = NULL; break; } curState->u.assertion.top = (char *)gData->backTrackSP - (char *)gData->backTrackStack; curState->u.assertion.sz = gData->cursz; curState->index = x->cp - gData->cpbegin; curState->parenSoFar = parenSoFar; PUSH_STATE_STACK(gData); if (!PushBackTrackState(gData, REOP_ASSERTTEST, nextpc, x, x->cp, 0, 0)) { return NULL; } continue; case REOP_ASSERT_NOT: nextpc = pc + GET_OFFSET(pc); pc += ARG_LEN; op = (REOp) *pc++; testpc = pc; if (REOP_IS_SIMPLE(op) /* Note - fail to fail! */ && SimpleMatch(gData, x, op, &testpc, JS_FALSE) && *testpc == REOP_ASSERTNOTTEST) { result = NULL; break; } curState->u.assertion.top = (char *)gData->backTrackSP - (char *)gData->backTrackStack; curState->u.assertion.sz = gData->cursz; curState->index = x->cp - gData->cpbegin; curState->parenSoFar = parenSoFar; PUSH_STATE_STACK(gData); if (!PushBackTrackState(gData, REOP_ASSERTNOTTEST, nextpc, x, x->cp, 0, 0)) { return NULL; } continue; case REOP_ASSERTTEST: --gData->stateStackTop; --curState; x->cp = gData->cpbegin + curState->index; gData->backTrackSP = (REBackTrackData *) ((char *)gData->backTrackStack + curState->u.assertion.top); gData->cursz = curState->u.assertion.sz; if (result) result = x; break; case REOP_ASSERTNOTTEST: --gData->stateStackTop; --curState; x->cp = gData->cpbegin + curState->index; gData->backTrackSP = (REBackTrackData *) ((char *)gData->backTrackStack + curState->u.assertion.top); gData->cursz = curState->u.assertion.sz; result = (!result) ? x : NULL; break; case REOP_END: if (x) return x; break; case REOP_STAR: curState->u.quantifier.min = 0; curState->u.quantifier.max = (uintN)-1; goto quantcommon; case REOP_PLUS: curState->u.quantifier.min = 1; curState->u.quantifier.max = (uintN)-1; goto quantcommon; case REOP_OPT: curState->u.quantifier.min = 0; curState->u.quantifier.max = 1; goto quantcommon; case REOP_QUANT: pc = ReadCompactIndex(pc, &k); curState->u.quantifier.min = k; pc = ReadCompactIndex(pc, &k); /* max is k - 1 to use one byte for (uintN)-1 sentinel. */ curState->u.quantifier.max = k - 1; JS_ASSERT(curState->u.quantifier.min <= curState->u.quantifier.max); quantcommon: if (curState->u.quantifier.max == 0) { pc = pc + GET_OFFSET(pc); op = (REOp) *pc++; result = x; continue; } /* Step over */ nextpc = pc + ARG_LEN; op = (REOp) *nextpc++; startcp = x->cp; if (REOP_IS_SIMPLE(op)) { if (!SimpleMatch(gData, x, op, &nextpc, JS_TRUE)) { if (curState->u.quantifier.min == 0) result = x; else result = NULL; pc = pc + GET_OFFSET(pc); break; } op = (REOp) *nextpc++; result = x; } curState->index = startcp - gData->cpbegin; curState->continue_op = REOP_REPEAT; curState->continue_pc = pc; curState->parenSoFar = parenSoFar; PUSH_STATE_STACK(gData); if (curState->u.quantifier.min == 0 && !PushBackTrackState(gData, REOP_REPEAT, pc, x, startcp, 0, 0)) { return NULL; } pc = nextpc; continue; case REOP_ENDCHILD: /* marks the end of a quantifier child */ pc = curState[-1].continue_pc; op = curState[-1].continue_op; continue; case REOP_REPEAT: CHECK_BRANCH(); --curState; do { --gData->stateStackTop; if (!result) { /* Failed, see if we have enough children. */ if (curState->u.quantifier.min == 0) goto repeatDone; goto break_switch; } if (curState->u.quantifier.min == 0 && x->cp == gData->cpbegin + curState->index) { /* matched an empty string, that'll get us nowhere */ result = NULL; goto break_switch; } if (curState->u.quantifier.min != 0) curState->u.quantifier.min--; if (curState->u.quantifier.max != (uintN) -1) curState->u.quantifier.max--; if (curState->u.quantifier.max == 0) goto repeatDone; nextpc = pc + ARG_LEN; nextop = (REOp) *nextpc; startcp = x->cp; if (REOP_IS_SIMPLE(nextop)) { nextpc++; if (!SimpleMatch(gData, x, nextop, &nextpc, JS_TRUE)) { if (curState->u.quantifier.min == 0) goto repeatDone; result = NULL; goto break_switch; } result = x; } curState->index = startcp - gData->cpbegin; PUSH_STATE_STACK(gData); if (curState->u.quantifier.min == 0 && !PushBackTrackState(gData, REOP_REPEAT, pc, x, startcp, curState->parenSoFar, parenSoFar - curState->parenSoFar)) { return NULL; } } while (*nextpc == REOP_ENDCHILD); pc = nextpc; op = (REOp) *pc++; parenSoFar = curState->parenSoFar; continue; repeatDone: result = x; pc += GET_OFFSET(pc); goto break_switch; case REOP_MINIMALSTAR: curState->u.quantifier.min = 0; curState->u.quantifier.max = (uintN)-1; goto minimalquantcommon; case REOP_MINIMALPLUS: curState->u.quantifier.min = 1; curState->u.quantifier.max = (uintN)-1; goto minimalquantcommon; case REOP_MINIMALOPT: curState->u.quantifier.min = 0; curState->u.quantifier.max = 1; goto minimalquantcommon; case REOP_MINIMALQUANT: pc = ReadCompactIndex(pc, &k); curState->u.quantifier.min = k; pc = ReadCompactIndex(pc, &k); /* See REOP_QUANT comments about k - 1. */ curState->u.quantifier.max = k - 1; JS_ASSERT(curState->u.quantifier.min <= curState->u.quantifier.max); minimalquantcommon: curState->index = x->cp - gData->cpbegin; curState->parenSoFar = parenSoFar; PUSH_STATE_STACK(gData); if (curState->u.quantifier.min != 0) { curState->continue_op = REOP_MINIMALREPEAT; curState->continue_pc = pc; /* step over */ pc += OFFSET_LEN; op = (REOp) *pc++; } else { if (!PushBackTrackState(gData, REOP_MINIMALREPEAT, pc, x, x->cp, 0, 0)) { return NULL; } --gData->stateStackTop; pc = pc + GET_OFFSET(pc); op = (REOp) *pc++; } continue; case REOP_MINIMALREPEAT: CHECK_BRANCH(); --gData->stateStackTop; --curState; if (!result) { /* * Non-greedy failure - try to consume another child. */ if (curState->u.quantifier.max == (uintN) -1 || curState->u.quantifier.max > 0) { curState->index = x->cp - gData->cpbegin; curState->continue_op = REOP_MINIMALREPEAT; curState->continue_pc = pc; pc += ARG_LEN; for (k = curState->parenSoFar; k < parenSoFar; k++) x->parens[k].index = -1; PUSH_STATE_STACK(gData); op = (REOp) *pc++; continue; } /* Don't need to adjust pc since we're going to pop. */ break; } if (curState->u.quantifier.min == 0 && x->cp == gData->cpbegin + curState->index) { /* Matched an empty string, that'll get us nowhere. */ result = NULL; break; } if (curState->u.quantifier.min != 0) curState->u.quantifier.min--; if (curState->u.quantifier.max != (uintN) -1) curState->u.quantifier.max--; if (curState->u.quantifier.min != 0) { curState->continue_op = REOP_MINIMALREPEAT; curState->continue_pc = pc; pc += ARG_LEN; for (k = curState->parenSoFar; k < parenSoFar; k++) x->parens[k].index = -1; curState->index = x->cp - gData->cpbegin; PUSH_STATE_STACK(gData); op = (REOp) *pc++; continue; } curState->index = x->cp - gData->cpbegin; curState->parenSoFar = parenSoFar; PUSH_STATE_STACK(gData); if (!PushBackTrackState(gData, REOP_MINIMALREPEAT, pc, x, x->cp, curState->parenSoFar, parenSoFar - curState->parenSoFar)) { return NULL; } --gData->stateStackTop; pc = pc + GET_OFFSET(pc); op = (REOp) *pc++; continue; default: JS_ASSERT(JS_FALSE); result = NULL; } break_switch:; } /* * If the match failed and there's a backtrack option, take it. * Otherwise this is a complete and utter failure. */ if (!result) { if (gData->cursz == 0) return NULL; backTrackData = gData->backTrackSP; gData->cursz = backTrackData->sz; gData->backTrackSP = (REBackTrackData *) ((char *)backTrackData - backTrackData->sz); x->cp = backTrackData->cp; pc = backTrackData->backtrack_pc; op = backTrackData->backtrack_op; gData->stateStackTop = backTrackData->saveStateStackTop; JS_ASSERT(gData->stateStackTop); memcpy(gData->stateStack, backTrackData + 1, sizeof(REProgState) * backTrackData->saveStateStackTop); curState = &gData->stateStack[gData->stateStackTop - 1]; if (backTrackData->parenCount) { memcpy(&x->parens[backTrackData->parenIndex], (char *)(backTrackData + 1) + sizeof(REProgState) * backTrackData->saveStateStackTop, sizeof(RECapture) * backTrackData->parenCount); parenSoFar = backTrackData->parenIndex + backTrackData->parenCount; } else { for (k = curState->parenSoFar; k < parenSoFar; k++) x->parens[k].index = -1; parenSoFar = curState->parenSoFar; } continue; } x = result; /* * Continue with the expression. */ op = (REOp)*pc++; } return NULL; } static REMatchState * MatchRegExp(REGlobalData *gData, REMatchState *x) { REMatchState *result; const jschar *cp = x->cp; const jschar *cp2; uintN j; /* * Have to include the position beyond the last character * in order to detect end-of-input/line condition. */ for (cp2 = cp; cp2 <= gData->cpend; cp2++) { gData->skipped = cp2 - cp; x->cp = cp2; for (j = 0; j < gData->regexp->parenCount; j++) x->parens[j].index = -1; result = ExecuteREBytecode(gData, x); if (!gData->ok || result) return result; gData->backTrackSP = gData->backTrackStack; gData->cursz = 0; gData->stateStackTop = 0; cp2 = cp + gData->skipped; } return NULL; } static REMatchState * InitMatch(JSContext *cx, REGlobalData *gData, JSRegExp *re) { REMatchState *result; uintN i; gData->backTrackStackSize = INITIAL_BACKTRACK; JS_ARENA_ALLOCATE_CAST(gData->backTrackStack, REBackTrackData *, &gData->pool, INITIAL_BACKTRACK); if (!gData->backTrackStack) goto bad; gData->backTrackSP = gData->backTrackStack; gData->cursz = 0; gData->stateStackLimit = INITIAL_STATESTACK; JS_ARENA_ALLOCATE_CAST(gData->stateStack, REProgState *, &gData->pool, sizeof(REProgState) * INITIAL_STATESTACK); if (!gData->stateStack) goto bad; gData->stateStackTop = 0; gData->cx = cx; gData->regexp = re; gData->ok = JS_TRUE; JS_ARENA_ALLOCATE_CAST(result, REMatchState *, &gData->pool, offsetof(REMatchState, parens) + re->parenCount * sizeof(RECapture)); if (!result) goto bad; for (i = 0; i < re->classCount; i++) { if (!re->classList[i].converted && !ProcessCharSet(gData, &re->classList[i])) { return NULL; } } return result; bad: JS_ReportOutOfMemory(cx); gData->ok = JS_FALSE; return NULL; } JSBool js_ExecuteRegExp(JSContext *cx, JSRegExp *re, JSString *str, size_t *indexp, JSBool test, jsval *rval) { REGlobalData gData; REMatchState *x, *result; const jschar *cp, *ep; size_t i, length, start; JSSubString *morepar; JSBool ok; JSRegExpStatics *res; ptrdiff_t matchlen; uintN num, morenum; JSString *parstr, *matchstr; JSObject *obj; RECapture *parsub = NULL; /* * It's safe to load from cp because JSStrings have a zero at the end, * and we never let cp get beyond cpend. */ start = *indexp; length = JSSTRING_LENGTH(str); if (start > length) start = length; cp = JSSTRING_CHARS(str); gData.cpbegin = cp; gData.cpend = cp + length; cp += start; gData.start = start; gData.skipped = 0; JS_InitArenaPool(&gData.pool, "RegExpPool", 8096, 4); x = InitMatch(cx, &gData, re); if (!x) { ok = JS_FALSE; goto out; } x->cp = cp; /* * Call the recursive matcher to do the real work. Return null on mismatch * whether testing or not. On match, return an extended Array object. */ result = MatchRegExp(&gData, x); ok = gData.ok; if (!ok) goto out; if (!result) { *rval = JSVAL_NULL; goto out; } cp = result->cp; i = cp - gData.cpbegin; *indexp = i; matchlen = i - (start + gData.skipped); ep = cp; cp -= matchlen; if (test) { /* * Testing for a match and updating cx->regExpStatics: don't allocate * an array object, do return true. */ *rval = JSVAL_TRUE; /* Avoid warning. (gcc doesn't detect that obj is needed iff !test); */ obj = NULL; } else { /* * The array returned on match has element 0 bound to the matched * string, elements 1 through state.parenCount bound to the paren * matches, an index property telling the length of the left context, * and an input property referring to the input string. */ obj = js_NewArrayObject(cx, 0, NULL); if (!obj) { ok = JS_FALSE; goto out; } *rval = OBJECT_TO_JSVAL(obj); #define DEFVAL(val, id) { \ ok = js_DefineProperty(cx, obj, id, val, \ JS_PropertyStub, JS_PropertyStub, \ JSPROP_ENUMERATE, NULL); \ if (!ok) { \ cx->weakRoots.newborn[GCX_OBJECT] = NULL; \ cx->weakRoots.newborn[GCX_STRING] = NULL; \ goto out; \ } \ } matchstr = js_NewStringCopyN(cx, cp, matchlen, 0); if (!matchstr) { cx->weakRoots.newborn[GCX_OBJECT] = NULL; ok = JS_FALSE; goto out; } DEFVAL(STRING_TO_JSVAL(matchstr), INT_TO_JSID(0)); } res = &cx->regExpStatics; res->input = str; res->parenCount = re->parenCount; if (re->parenCount == 0) { res->lastParen = js_EmptySubString; } else { for (num = 0; num < re->parenCount; num++) { parsub = &result->parens[num]; if (num < 9) { if (parsub->index == -1) { res->parens[num].chars = NULL; res->parens[num].length = 0; } else { res->parens[num].chars = gData.cpbegin + parsub->index; res->parens[num].length = parsub->length; } } else { morenum = num - 9; morepar = res->moreParens; if (!morepar) { res->moreLength = 10; morepar = (JSSubString*) JS_malloc(cx, 10 * sizeof(JSSubString)); } else if (morenum >= res->moreLength) { res->moreLength += 10; morepar = (JSSubString*) JS_realloc(cx, morepar, res->moreLength * sizeof(JSSubString)); } if (!morepar) { cx->weakRoots.newborn[GCX_OBJECT] = NULL; cx->weakRoots.newborn[GCX_STRING] = NULL; ok = JS_FALSE; goto out; } res->moreParens = morepar; if (parsub->index == -1) { morepar[morenum].chars = NULL; morepar[morenum].length = 0; } else { morepar[morenum].chars = gData.cpbegin + parsub->index; morepar[morenum].length = parsub->length; } } if (test) continue; if (parsub->index == -1) { ok = js_DefineProperty(cx, obj, INT_TO_JSID(num + 1), JSVAL_VOID, NULL, NULL, JSPROP_ENUMERATE, NULL); } else { parstr = js_NewStringCopyN(cx, gData.cpbegin + parsub->index, parsub->length, 0); if (!parstr) { cx->weakRoots.newborn[GCX_OBJECT] = NULL; cx->weakRoots.newborn[GCX_STRING] = NULL; ok = JS_FALSE; goto out; } ok = js_DefineProperty(cx, obj, INT_TO_JSID(num + 1), STRING_TO_JSVAL(parstr), NULL, NULL, JSPROP_ENUMERATE, NULL); } if (!ok) { cx->weakRoots.newborn[GCX_OBJECT] = NULL; cx->weakRoots.newborn[GCX_STRING] = NULL; goto out; } } if (parsub->index == -1) { res->lastParen = js_EmptySubString; } else { res->lastParen.chars = gData.cpbegin + parsub->index; res->lastParen.length = parsub->length; } } if (!test) { /* * Define the index and input properties last for better for/in loop * order (so they come after the elements). */ DEFVAL(INT_TO_JSVAL(start + gData.skipped), ATOM_TO_JSID(cx->runtime->atomState.indexAtom)); DEFVAL(STRING_TO_JSVAL(str), ATOM_TO_JSID(cx->runtime->atomState.inputAtom)); } #undef DEFVAL res->lastMatch.chars = cp; res->lastMatch.length = matchlen; /* * For JS1.3 and ECMAv2, emulate Perl5 exactly: * * js1.3 "hi", "hi there" "hihitherehi therebye" */ res->leftContext.chars = JSSTRING_CHARS(str); res->leftContext.length = start + gData.skipped; res->rightContext.chars = ep; res->rightContext.length = gData.cpend - ep; out: JS_FinishArenaPool(&gData.pool); return ok; } /************************************************************************/ enum regexp_tinyid { REGEXP_SOURCE = -1, REGEXP_GLOBAL = -2, REGEXP_IGNORE_CASE = -3, REGEXP_LAST_INDEX = -4, REGEXP_MULTILINE = -5 }; #define REGEXP_PROP_ATTRS (JSPROP_PERMANENT|JSPROP_SHARED) static JSPropertySpec regexp_props[] = { {"source", REGEXP_SOURCE, REGEXP_PROP_ATTRS | JSPROP_READONLY,0,0}, {"global", REGEXP_GLOBAL, REGEXP_PROP_ATTRS | JSPROP_READONLY,0,0}, {"ignoreCase", REGEXP_IGNORE_CASE, REGEXP_PROP_ATTRS | JSPROP_READONLY,0,0}, {"lastIndex", REGEXP_LAST_INDEX, REGEXP_PROP_ATTRS,0,0}, {"multiline", REGEXP_MULTILINE, REGEXP_PROP_ATTRS | JSPROP_READONLY,0,0}, {0,0,0,0,0} }; static JSBool regexp_getProperty(JSContext *cx, JSObject *obj, jsval id, jsval *vp) { jsint slot; JSRegExp *re; if (!JSVAL_IS_INT(id)) return JS_TRUE; slot = JSVAL_TO_INT(id); if (slot == REGEXP_LAST_INDEX) return JS_GetReservedSlot(cx, obj, 0, vp); JS_LOCK_OBJ(cx, obj); re = (JSRegExp *) JS_GetInstancePrivate(cx, obj, &js_RegExpClass, NULL); if (re) { switch (slot) { case REGEXP_SOURCE: *vp = STRING_TO_JSVAL(re->source); break; case REGEXP_GLOBAL: *vp = BOOLEAN_TO_JSVAL((re->flags & JSREG_GLOB) != 0); break; case REGEXP_IGNORE_CASE: *vp = BOOLEAN_TO_JSVAL((re->flags & JSREG_FOLD) != 0); break; case REGEXP_MULTILINE: *vp = BOOLEAN_TO_JSVAL((re->flags & JSREG_MULTILINE) != 0); break; } } JS_UNLOCK_OBJ(cx, obj); return JS_TRUE; } static JSBool regexp_setProperty(JSContext *cx, JSObject *obj, jsval id, jsval *vp) { JSBool ok; jsint slot; jsdouble lastIndex; ok = JS_TRUE; if (!JSVAL_IS_INT(id)) return ok; slot = JSVAL_TO_INT(id); if (slot == REGEXP_LAST_INDEX) { if (!js_ValueToNumber(cx, *vp, &lastIndex)) return JS_FALSE; lastIndex = js_DoubleToInteger(lastIndex); ok = js_NewNumberValue(cx, lastIndex, vp) && JS_SetReservedSlot(cx, obj, 0, *vp); } return ok; } /* * RegExp class static properties and their Perl counterparts: * * RegExp.input $_ * RegExp.multiline $* * RegExp.lastMatch $& * RegExp.lastParen $+ * RegExp.leftContext $` * RegExp.rightContext $' */ enum regexp_static_tinyid { REGEXP_STATIC_INPUT = -1, REGEXP_STATIC_MULTILINE = -2, REGEXP_STATIC_LAST_MATCH = -3, REGEXP_STATIC_LAST_PAREN = -4, REGEXP_STATIC_LEFT_CONTEXT = -5, REGEXP_STATIC_RIGHT_CONTEXT = -6 }; JSBool js_InitRegExpStatics(JSContext *cx, JSRegExpStatics *res) { JS_ClearRegExpStatics(cx); return js_AddRoot(cx, &res->input, "res->input"); } void js_FreeRegExpStatics(JSContext *cx, JSRegExpStatics *res) { if (res->moreParens) { JS_free(cx, res->moreParens); res->moreParens = NULL; } js_RemoveRoot(cx->runtime, &res->input); } static JSBool regexp_static_getProperty(JSContext *cx, JSObject *obj, jsval id, jsval *vp) { jsint slot; JSRegExpStatics *res; JSString *str; JSSubString *sub; res = &cx->regExpStatics; if (!JSVAL_IS_INT(id)) return JS_TRUE; slot = JSVAL_TO_INT(id); switch (slot) { case REGEXP_STATIC_INPUT: *vp = res->input ? STRING_TO_JSVAL(res->input) : JS_GetEmptyStringValue(cx); return JS_TRUE; case REGEXP_STATIC_MULTILINE: *vp = BOOLEAN_TO_JSVAL(res->multiline); return JS_TRUE; case REGEXP_STATIC_LAST_MATCH: sub = &res->lastMatch; break; case REGEXP_STATIC_LAST_PAREN: sub = &res->lastParen; break; case REGEXP_STATIC_LEFT_CONTEXT: sub = &res->leftContext; break; case REGEXP_STATIC_RIGHT_CONTEXT: sub = &res->rightContext; break; default: sub = REGEXP_PAREN_SUBSTRING(res, slot); break; } str = js_NewStringCopyN(cx, sub->chars, sub->length, 0); if (!str) return JS_FALSE; *vp = STRING_TO_JSVAL(str); return JS_TRUE; } static JSBool regexp_static_setProperty(JSContext *cx, JSObject *obj, jsval id, jsval *vp) { JSRegExpStatics *res; if (!JSVAL_IS_INT(id)) return JS_TRUE; res = &cx->regExpStatics; /* XXX use if-else rather than switch to keep MSVC1.52 from crashing */ if (JSVAL_TO_INT(id) == REGEXP_STATIC_INPUT) { if (!JSVAL_IS_STRING(*vp) && !JS_ConvertValue(cx, *vp, JSTYPE_STRING, vp)) { return JS_FALSE; } res->input = JSVAL_TO_STRING(*vp); } else if (JSVAL_TO_INT(id) == REGEXP_STATIC_MULTILINE) { if (!JSVAL_IS_BOOLEAN(*vp) && !JS_ConvertValue(cx, *vp, JSTYPE_BOOLEAN, vp)) { return JS_FALSE; } res->multiline = JSVAL_TO_BOOLEAN(*vp); } return JS_TRUE; } static JSPropertySpec regexp_static_props[] = { {"input", REGEXP_STATIC_INPUT, JSPROP_ENUMERATE|JSPROP_SHARED, regexp_static_getProperty, regexp_static_setProperty}, {"multiline", REGEXP_STATIC_MULTILINE, JSPROP_ENUMERATE|JSPROP_SHARED, regexp_static_getProperty, regexp_static_setProperty}, {"lastMatch", REGEXP_STATIC_LAST_MATCH, JSPROP_ENUMERATE|JSPROP_READONLY|JSPROP_SHARED, regexp_static_getProperty, regexp_static_getProperty}, {"lastParen", REGEXP_STATIC_LAST_PAREN, JSPROP_ENUMERATE|JSPROP_READONLY|JSPROP_SHARED, regexp_static_getProperty, regexp_static_getProperty}, {"leftContext", REGEXP_STATIC_LEFT_CONTEXT, JSPROP_ENUMERATE|JSPROP_READONLY|JSPROP_SHARED, regexp_static_getProperty, regexp_static_getProperty}, {"rightContext", REGEXP_STATIC_RIGHT_CONTEXT, JSPROP_ENUMERATE|JSPROP_READONLY|JSPROP_SHARED, regexp_static_getProperty, regexp_static_getProperty}, /* XXX should have block scope and local $1, etc. */ {"$1", 0, JSPROP_ENUMERATE|JSPROP_READONLY|JSPROP_SHARED, regexp_static_getProperty, regexp_static_getProperty}, {"$2", 1, JSPROP_ENUMERATE|JSPROP_READONLY|JSPROP_SHARED, regexp_static_getProperty, regexp_static_getProperty}, {"$3", 2, JSPROP_ENUMERATE|JSPROP_READONLY|JSPROP_SHARED, regexp_static_getProperty, regexp_static_getProperty}, {"$4", 3, JSPROP_ENUMERATE|JSPROP_READONLY|JSPROP_SHARED, regexp_static_getProperty, regexp_static_getProperty}, {"$5", 4, JSPROP_ENUMERATE|JSPROP_READONLY|JSPROP_SHARED, regexp_static_getProperty, regexp_static_getProperty}, {"$6", 5, JSPROP_ENUMERATE|JSPROP_READONLY|JSPROP_SHARED, regexp_static_getProperty, regexp_static_getProperty}, {"$7", 6, JSPROP_ENUMERATE|JSPROP_READONLY|JSPROP_SHARED, regexp_static_getProperty, regexp_static_getProperty}, {"$8", 7, JSPROP_ENUMERATE|JSPROP_READONLY|JSPROP_SHARED, regexp_static_getProperty, regexp_static_getProperty}, {"$9", 8, JSPROP_ENUMERATE|JSPROP_READONLY|JSPROP_SHARED, regexp_static_getProperty, regexp_static_getProperty}, {0,0,0,0,0} }; static void regexp_finalize(JSContext *cx, JSObject *obj) { JSRegExp *re; re = (JSRegExp *) JS_GetPrivate(cx, obj); if (!re) return; js_DestroyRegExp(cx, re); } /* Forward static prototype. */ static JSBool regexp_exec(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval); static JSBool regexp_call(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) { return regexp_exec(cx, JSVAL_TO_OBJECT(argv[-2]), argc, argv, rval); } #if JS_HAS_XDR #include "jsxdrapi.h" static JSBool regexp_xdrObject(JSXDRState *xdr, JSObject **objp) { JSRegExp *re; JSString *source; uint32 flagsword; JSObject *obj; if (xdr->mode == JSXDR_ENCODE) { re = (JSRegExp *) JS_GetPrivate(xdr->cx, *objp); if (!re) return JS_FALSE; source = re->source; flagsword = ((uint32)re->cloneIndex << 16) | re->flags; } if (!JS_XDRString(xdr, &source) || !JS_XDRUint32(xdr, &flagsword)) { return JS_FALSE; } if (xdr->mode == JSXDR_DECODE) { obj = js_NewObject(xdr->cx, &js_RegExpClass, NULL, NULL); if (!obj) return JS_FALSE; re = js_NewRegExp(xdr->cx, NULL, source, (uint16)flagsword, JS_FALSE); if (!re) return JS_FALSE; if (!JS_SetPrivate(xdr->cx, obj, re) || !js_SetLastIndex(xdr->cx, obj, 0)) { js_DestroyRegExp(xdr->cx, re); return JS_FALSE; } re->cloneIndex = (uint16)(flagsword >> 16); *objp = obj; } return JS_TRUE; } #else /* !JS_HAS_XDR */ #define regexp_xdrObject NULL #endif /* !JS_HAS_XDR */ static uint32 regexp_mark(JSContext *cx, JSObject *obj, void *arg) { JSRegExp *re = (JSRegExp *) JS_GetPrivate(cx, obj); if (re) GC_MARK(cx, re->source, "source"); return 0; } JSClass js_RegExpClass = { js_RegExp_str, JSCLASS_HAS_PRIVATE | JSCLASS_HAS_RESERVED_SLOTS(1) | JSCLASS_HAS_CACHED_PROTO(JSProto_RegExp), JS_PropertyStub, JS_PropertyStub, regexp_getProperty, regexp_setProperty, JS_EnumerateStub, JS_ResolveStub, JS_ConvertStub, regexp_finalize, NULL, NULL, regexp_call, NULL, regexp_xdrObject, NULL, regexp_mark, 0 }; static const jschar empty_regexp_ucstr[] = {'(', '?', ':', ')', 0}; JSBool js_regexp_toString(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) { JSRegExp *re; const jschar *source; jschar *chars; size_t length, nflags; uintN flags; JSString *str; if (!JS_InstanceOf(cx, obj, &js_RegExpClass, argv)) return JS_FALSE; JS_LOCK_OBJ(cx, obj); re = (JSRegExp *) JS_GetPrivate(cx, obj); if (!re) { JS_UNLOCK_OBJ(cx, obj); *rval = STRING_TO_JSVAL(cx->runtime->emptyString); return JS_TRUE; } source = JSSTRING_CHARS(re->source); length = JSSTRING_LENGTH(re->source); if (length == 0) { source = empty_regexp_ucstr; length = sizeof(empty_regexp_ucstr) / sizeof(jschar) - 1; } length += 2; nflags = 0; for (flags = re->flags; flags != 0; flags &= flags - 1) nflags++; chars = (jschar*) JS_malloc(cx, (length + nflags + 1) * sizeof(jschar)); if (!chars) { JS_UNLOCK_OBJ(cx, obj); return JS_FALSE; } chars[0] = '/'; js_strncpy(&chars[1], source, length - 2); chars[length-1] = '/'; if (nflags) { if (re->flags & JSREG_GLOB) chars[length++] = 'g'; if (re->flags & JSREG_FOLD) chars[length++] = 'i'; if (re->flags & JSREG_MULTILINE) chars[length++] = 'm'; } JS_UNLOCK_OBJ(cx, obj); chars[length] = 0; str = js_NewString(cx, chars, length, 0); if (!str) { JS_free(cx, chars); return JS_FALSE; } *rval = STRING_TO_JSVAL(str); return JS_TRUE; } static JSBool regexp_compile(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) { JSString *opt, *str; JSRegExp *oldre, *re; JSBool ok, ok2; JSObject *obj2; size_t length, nbytes; const jschar *cp, *start, *end; jschar *nstart, *ncp, *tmp; if (!JS_InstanceOf(cx, obj, &js_RegExpClass, argv)) return JS_FALSE; opt = NULL; if (argc == 0) { str = cx->runtime->emptyString; } else { if (JSVAL_IS_OBJECT(argv[0])) { /* * If we get passed in a RegExp object we construct a new * RegExp that is a duplicate of it by re-compiling the * original source code. ECMA requires that it be an error * here if the flags are specified. (We must use the flags * from the original RegExp also). */ obj2 = JSVAL_TO_OBJECT(argv[0]); if (obj2 && OBJ_GET_CLASS(cx, obj2) == &js_RegExpClass) { if (argc >= 2 && !JSVAL_IS_VOID(argv[1])) { /* 'flags' passed */ JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_NEWREGEXP_FLAGGED); return JS_FALSE; } JS_LOCK_OBJ(cx, obj2); re = (JSRegExp *) JS_GetPrivate(cx, obj2); if (!re) { JS_UNLOCK_OBJ(cx, obj2); return JS_FALSE; } re = js_NewRegExp(cx, NULL, re->source, re->flags, JS_FALSE); JS_UNLOCK_OBJ(cx, obj2); goto created; } } str = js_ValueToString(cx, argv[0]); if (!str) return JS_FALSE; argv[0] = STRING_TO_JSVAL(str); if (argc > 1) { if (JSVAL_IS_VOID(argv[1])) { opt = NULL; } else { opt = js_ValueToString(cx, argv[1]); if (!opt) return JS_FALSE; argv[1] = STRING_TO_JSVAL(opt); } } /* Escape any naked slashes in the regexp source. */ length = JSSTRING_LENGTH(str); start = JSSTRING_CHARS(str); end = start + length; nstart = ncp = NULL; for (cp = start; cp < end; cp++) { if (*cp == '/' && (cp == start || cp[-1] != '\\')) { nbytes = (++length + 1) * sizeof(jschar); if (!nstart) { nstart = (jschar *) JS_malloc(cx, nbytes); if (!nstart) return JS_FALSE; ncp = nstart + (cp - start); js_strncpy(nstart, start, cp - start); } else { tmp = (jschar *) JS_realloc(cx, nstart, nbytes); if (!tmp) { JS_free(cx, nstart); return JS_FALSE; } ncp = tmp + (ncp - nstart); nstart = tmp; } *ncp++ = '\\'; } if (nstart) *ncp++ = *cp; } if (nstart) { /* Don't forget to store the backstop after the new string. */ JS_ASSERT((size_t)(ncp - nstart) == length); *ncp = 0; str = js_NewString(cx, nstart, length, 0); if (!str) { JS_free(cx, nstart); return JS_FALSE; } argv[0] = STRING_TO_JSVAL(str); } } re = js_NewRegExpOpt(cx, NULL, str, opt, JS_FALSE); created: if (!re) return JS_FALSE; JS_LOCK_OBJ(cx, obj); oldre = (JSRegExp *) JS_GetPrivate(cx, obj); ok = JS_SetPrivate(cx, obj, re); ok2 = js_SetLastIndex(cx, obj, 0); JS_UNLOCK_OBJ(cx, obj); if (!ok) { js_DestroyRegExp(cx, re); return JS_FALSE; } if (oldre) js_DestroyRegExp(cx, oldre); *rval = OBJECT_TO_JSVAL(obj); return ok2; } static JSBool regexp_exec_sub(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, JSBool test, jsval *rval) { JSBool ok; JSRegExp *re; jsdouble lastIndex; JSString *str; size_t i; ok = JS_InstanceOf(cx, obj, &js_RegExpClass, argv); if (!ok) return JS_FALSE; JS_LOCK_OBJ(cx, obj); re = (JSRegExp *) JS_GetPrivate(cx, obj); if (!re) { JS_UNLOCK_OBJ(cx, obj); return JS_TRUE; } /* NB: we must reach out: after this paragraph, in order to drop re. */ HOLD_REGEXP(cx, re); if (re->flags & JSREG_GLOB) { ok = js_GetLastIndex(cx, obj, &lastIndex); } else { lastIndex = 0; } JS_UNLOCK_OBJ(cx, obj); if (!ok) goto out; /* Now that obj is unlocked, it's safe to (potentially) grab the GC lock. */ if (argc == 0) { str = cx->regExpStatics.input; if (!str) { JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_NO_INPUT, JS_GetStringBytes(re->source), (re->flags & JSREG_GLOB) ? "g" : "", (re->flags & JSREG_FOLD) ? "i" : "", (re->flags & JSREG_MULTILINE) ? "m" : ""); ok = JS_FALSE; goto out; } } else { str = js_ValueToString(cx, argv[0]); if (!str) { ok = JS_FALSE; goto out; } argv[0] = STRING_TO_JSVAL(str); } if (lastIndex < 0 || JSSTRING_LENGTH(str) < lastIndex) { ok = js_SetLastIndex(cx, obj, 0); *rval = JSVAL_NULL; } else { i = (size_t) lastIndex; ok = js_ExecuteRegExp(cx, re, str, &i, test, rval); if (ok && (re->flags & JSREG_GLOB)) ok = js_SetLastIndex(cx, obj, (*rval == JSVAL_NULL) ? 0 : i); } out: DROP_REGEXP(cx, re); return ok; } static JSBool regexp_exec(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) { return regexp_exec_sub(cx, obj, argc, argv, JS_FALSE, rval); } static JSBool regexp_test(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) { if (!regexp_exec_sub(cx, obj, argc, argv, JS_TRUE, rval)) return JS_FALSE; if (*rval != JSVAL_TRUE) *rval = JSVAL_FALSE; return JS_TRUE; } static JSFunctionSpec regexp_methods[] = { #if JS_HAS_TOSOURCE {js_toSource_str, js_regexp_toString, 0,0,0}, #endif {js_toString_str, js_regexp_toString, 0,0,0}, {"compile", regexp_compile, 1,0,0}, {"exec", regexp_exec, 0,0,0}, {"test", regexp_test, 0,0,0}, {0,0,0,0,0} }; static JSBool RegExp(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) { if (!(cx->fp->flags & JSFRAME_CONSTRUCTING)) { /* * If first arg is regexp and no flags are given, just return the arg. * (regexp_compile detects the regexp + flags case and throws a * TypeError.) See 10.15.3.1. */ if ((argc < 2 || JSVAL_IS_VOID(argv[1])) && !JSVAL_IS_PRIMITIVE(argv[0]) && OBJ_GET_CLASS(cx, JSVAL_TO_OBJECT(argv[0])) == &js_RegExpClass) { *rval = argv[0]; return JS_TRUE; } /* Otherwise, replace obj with a new RegExp object. */ obj = js_NewObject(cx, &js_RegExpClass, NULL, NULL); if (!obj) return JS_FALSE; /* * regexp_compile does not use rval to root its temporaries * so we can use it to root obj. */ *rval = OBJECT_TO_JSVAL(obj); } return regexp_compile(cx, obj, argc, argv, rval); } JSObject * js_InitRegExpClass(JSContext *cx, JSObject *obj) { JSObject *proto, *ctor; jsval rval; proto = JS_InitClass(cx, obj, NULL, &js_RegExpClass, RegExp, 1, regexp_props, regexp_methods, regexp_static_props, NULL); if (!proto || !(ctor = JS_GetConstructor(cx, proto))) return NULL; if (!JS_AliasProperty(cx, ctor, "input", "$_") || !JS_AliasProperty(cx, ctor, "multiline", "$*") || !JS_AliasProperty(cx, ctor, "lastMatch", "$&") || !JS_AliasProperty(cx, ctor, "lastParen", "$+") || !JS_AliasProperty(cx, ctor, "leftContext", "$`") || !JS_AliasProperty(cx, ctor, "rightContext", "$'")) { goto bad; } /* Give RegExp.prototype private data so it matches the empty string. */ if (!regexp_compile(cx, proto, 0, NULL, &rval)) goto bad; return proto; bad: JS_DeleteProperty(cx, obj, js_RegExpClass.name); return NULL; } JSObject * js_NewRegExpObject(JSContext *cx, JSTokenStream *ts, jschar *chars, size_t length, uintN flags) { JSString *str; JSObject *obj; JSRegExp *re; JSTempValueRooter tvr; str = js_NewStringCopyN(cx, chars, length, 0); if (!str) return NULL; re = js_NewRegExp(cx, ts, str, flags, JS_FALSE); if (!re) return NULL; JS_PUSH_TEMP_ROOT_STRING(cx, str, &tvr); obj = js_NewObject(cx, &js_RegExpClass, NULL, NULL); if (!obj || !JS_SetPrivate(cx, obj, re)) { js_DestroyRegExp(cx, re); obj = NULL; } if (obj && !js_SetLastIndex(cx, obj, 0)) obj = NULL; JS_POP_TEMP_ROOT(cx, &tvr); return obj; } JSObject * js_CloneRegExpObject(JSContext *cx, JSObject *obj, JSObject *parent) { JSObject *clone; JSRegExp *re; JS_ASSERT(OBJ_GET_CLASS(cx, obj) == &js_RegExpClass); clone = js_NewObject(cx, &js_RegExpClass, NULL, parent); if (!clone) return NULL; re = JS_GetPrivate(cx, obj); if (!JS_SetPrivate(cx, clone, re) || !js_SetLastIndex(cx, clone, 0)) { cx->weakRoots.newborn[GCX_OBJECT] = NULL; return NULL; } HOLD_REGEXP(cx, re); return clone; } JSBool js_GetLastIndex(JSContext *cx, JSObject *obj, jsdouble *lastIndex) { jsval v; return JS_GetReservedSlot(cx, obj, 0, &v) && js_ValueToNumber(cx, v, lastIndex); } JSBool js_SetLastIndex(JSContext *cx, JSObject *obj, jsdouble lastIndex) { jsval v; return js_NewNumberValue(cx, lastIndex, &v) && JS_SetReservedSlot(cx, obj, 0, v); } pacparser-1.4.5/src/spidermonkey/js/src/jsregexp.h000066400000000000000000000152521464010763600222200ustar00rootroot00000000000000/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- * * ***** BEGIN LICENSE BLOCK ***** * Version: MPL 1.1/GPL 2.0/LGPL 2.1 * * The contents of this file are subject to the Mozilla Public License Version * 1.1 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * http://www.mozilla.org/MPL/ * * Software distributed under the License is distributed on an "AS IS" basis, * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License * for the specific language governing rights and limitations under the * License. * * The Original Code is Mozilla Communicator client code, released * March 31, 1998. * * The Initial Developer of the Original Code is * Netscape Communications Corporation. * Portions created by the Initial Developer are Copyright (C) 1998 * the Initial Developer. All Rights Reserved. * * Contributor(s): * * Alternatively, the contents of this file may be used under the terms of * either of the GNU General Public License Version 2 or later (the "GPL"), * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), * in which case the provisions of the GPL or the LGPL are applicable instead * of those above. If you wish to allow use of your version of this file only * under the terms of either the GPL or the LGPL, and not to allow others to * use your version of this file under the terms of the MPL, indicate your * decision by deleting the provisions above and replace them with the notice * and other provisions required by the GPL or the LGPL. If you do not delete * the provisions above, a recipient may use your version of this file under * the terms of any one of the MPL, the GPL or the LGPL. * * ***** END LICENSE BLOCK ***** */ #ifndef jsregexp_h___ #define jsregexp_h___ /* * JS regular expression interface. */ #include #include "jspubtd.h" #include "jsstr.h" #ifdef JS_THREADSAFE #include "jsdhash.h" #endif struct JSRegExpStatics { JSString *input; /* input string to match (perl $_, GC root) */ JSBool multiline; /* whether input contains newlines (perl $*) */ uint16 parenCount; /* number of valid elements in parens[] */ uint16 moreLength; /* number of allocated elements in moreParens */ JSSubString parens[9]; /* last set of parens matched (perl $1, $2) */ JSSubString *moreParens; /* null or realloc'd vector for $10, etc. */ JSSubString lastMatch; /* last string matched (perl $&) */ JSSubString lastParen; /* last paren matched (perl $+) */ JSSubString leftContext; /* input to left of last match (perl $`) */ JSSubString rightContext; /* input to right of last match (perl $') */ }; /* * This struct holds a bitmap representation of a class from a regexp. * There's a list of these referenced by the classList field in the JSRegExp * struct below. The initial state has startIndex set to the offset in the * original regexp source of the beginning of the class contents. The first * use of the class converts the source representation into a bitmap. * */ typedef struct RECharSet { JSPackedBool converted; JSPackedBool sense; uint16 length; union { uint8 *bits; struct { size_t startIndex; size_t length; } src; } u; } RECharSet; /* * This macro is safe because moreParens is guaranteed to be allocated and big * enough to hold parenCount, or else be null when parenCount is 0. */ #define REGEXP_PAREN_SUBSTRING(res, num) \ (((jsuint)(num) < (jsuint)(res)->parenCount) \ ? ((jsuint)(num) < 9) \ ? &(res)->parens[num] \ : &(res)->moreParens[(num) - 9] \ : &js_EmptySubString) typedef struct RENode RENode; struct JSRegExp { jsrefcount nrefs; /* reference count */ uint16 flags; /* flags, see jsapi.h's JSREG_* defines */ uint16 cloneIndex; /* index in fp->vars or funobj->slots of cloned regexp object */ size_t parenCount; /* number of parenthesized submatches */ size_t classCount; /* count [...] bitmaps */ RECharSet *classList; /* list of [...] bitmaps */ JSString *source; /* locked source string, sans // */ jsbytecode program[1]; /* regular expression bytecode */ }; extern JSRegExp * js_NewRegExp(JSContext *cx, JSTokenStream *ts, JSString *str, uintN flags, JSBool flat); extern JSRegExp * js_NewRegExpOpt(JSContext *cx, JSTokenStream *ts, JSString *str, JSString *opt, JSBool flat); #define HOLD_REGEXP(cx, re) JS_ATOMIC_INCREMENT(&(re)->nrefs) #define DROP_REGEXP(cx, re) js_DestroyRegExp(cx, re) extern void js_DestroyRegExp(JSContext *cx, JSRegExp *re); /* * Execute re on input str at *indexp, returning null in *rval on mismatch. * On match, return true if test is true, otherwise return an array object. * Update *indexp and cx->regExpStatics always on match. */ extern JSBool js_ExecuteRegExp(JSContext *cx, JSRegExp *re, JSString *str, size_t *indexp, JSBool test, jsval *rval); /* * These two add and remove GC roots, respectively, so their calls must be * well-ordered. */ extern JSBool js_InitRegExpStatics(JSContext *cx, JSRegExpStatics *res); extern void js_FreeRegExpStatics(JSContext *cx, JSRegExpStatics *res); #define JSVAL_IS_REGEXP(cx, v) \ (JSVAL_IS_OBJECT(v) && JSVAL_TO_OBJECT(v) && \ OBJ_GET_CLASS(cx, JSVAL_TO_OBJECT(v)) == &js_RegExpClass) extern JSClass js_RegExpClass; extern JSObject * js_InitRegExpClass(JSContext *cx, JSObject *obj); /* * Export js_regexp_toString to the decompiler. */ extern JSBool js_regexp_toString(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval); /* * Create, serialize/deserialize, or clone a RegExp object. */ extern JSObject * js_NewRegExpObject(JSContext *cx, JSTokenStream *ts, jschar *chars, size_t length, uintN flags); extern JSBool js_XDRRegExp(JSXDRState *xdr, JSObject **objp); extern JSObject * js_CloneRegExpObject(JSContext *cx, JSObject *obj, JSObject *parent); /* * Get and set the per-object (clone or clone-parent) lastIndex slot. */ extern JSBool js_GetLastIndex(JSContext *cx, JSObject *obj, jsdouble *lastIndex); extern JSBool js_SetLastIndex(JSContext *cx, JSObject *obj, jsdouble lastIndex); #endif /* jsregexp_h___ */ pacparser-1.4.5/src/spidermonkey/js/src/jsscan.c000066400000000000000000002015571464010763600216520ustar00rootroot00000000000000/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- * vim: set sw=4 ts=8 et tw=78: * * ***** BEGIN LICENSE BLOCK ***** * Version: MPL 1.1/GPL 2.0/LGPL 2.1 * * The contents of this file are subject to the Mozilla Public License Version * 1.1 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * http://www.mozilla.org/MPL/ * * Software distributed under the License is distributed on an "AS IS" basis, * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License * for the specific language governing rights and limitations under the * License. * * The Original Code is Mozilla Communicator client code, released * March 31, 1998. * * The Initial Developer of the Original Code is * Netscape Communications Corporation. * Portions created by the Initial Developer are Copyright (C) 1998 * the Initial Developer. All Rights Reserved. * * Contributor(s): * * Alternatively, the contents of this file may be used under the terms of * either of the GNU General Public License Version 2 or later (the "GPL"), * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), * in which case the provisions of the GPL or the LGPL are applicable instead * of those above. If you wish to allow use of your version of this file only * under the terms of either the GPL or the LGPL, and not to allow others to * use your version of this file under the terms of the MPL, indicate your * decision by deleting the provisions above and replace them with the notice * and other provisions required by the GPL or the LGPL. If you do not delete * the provisions above, a recipient may use your version of this file under * the terms of any one of the MPL, the GPL or the LGPL. * * ***** END LICENSE BLOCK ***** */ /* * JS lexical scanner. */ #include "jsstddef.h" #include /* first to avoid trouble on some systems */ #include #include #include #ifdef HAVE_MEMORY_H #include #endif #include #include #include #include "jstypes.h" #include "jsarena.h" /* Added by JSIFY */ #include "jsutil.h" /* Added by JSIFY */ #include "jsdtoa.h" #include "jsprf.h" #include "jsapi.h" #include "jsatom.h" #include "jscntxt.h" #include "jsconfig.h" #include "jsemit.h" #include "jsexn.h" #include "jsnum.h" #include "jsopcode.h" #include "jsregexp.h" #include "jsscan.h" #include "jsscript.h" #if JS_HAS_XML_SUPPORT #include "jsparse.h" #include "jsxml.h" #endif #define JS_KEYWORD(keyword, type, op, version) \ const char js_##keyword##_str[] = #keyword; #include "jskeyword.tbl" #undef JS_KEYWORD struct keyword { const char *chars; /* C string with keyword text */ JSTokenType tokentype; /* JSTokenType */ JSOp op; /* JSOp */ JSVersion version; /* JSVersion */ }; static const struct keyword keyword_defs[] = { #define JS_KEYWORD(keyword, type, op, version) \ {js_##keyword##_str, type, op, version}, #include "jskeyword.tbl" #undef JS_KEYWORD }; #define KEYWORD_COUNT (sizeof keyword_defs / sizeof keyword_defs[0]) static const struct keyword * FindKeyword(const jschar *s, size_t length) { register size_t i; const struct keyword *kw; const char *chars; JS_ASSERT(length != 0); #define JSKW_LENGTH() length #define JSKW_AT(column) s[column] #define JSKW_GOT_MATCH(index) i = (index); goto got_match; #define JSKW_TEST_GUESS(index) i = (index); goto test_guess; #define JSKW_NO_MATCH() goto no_match; #include "jsautokw.h" #undef JSKW_NO_MATCH #undef JSKW_TEST_GUESS #undef JSKW_GOT_MATCH #undef JSKW_AT #undef JSKW_LENGTH got_match: return &keyword_defs[i]; test_guess: kw = &keyword_defs[i]; chars = kw->chars; do { if (*s++ != (unsigned char)(*chars++)) goto no_match; } while (--length != 0); return kw; no_match: return NULL; } JSTokenType js_CheckKeyword(const jschar *str, size_t length) { const struct keyword *kw; JS_ASSERT(length != 0); kw = FindKeyword(str, length); return kw ? kw->tokentype : TOK_EOF; } JS_FRIEND_API(void) js_MapKeywords(void (*mapfun)(const char *)) { size_t i; for (i = 0; i != KEYWORD_COUNT; ++i) mapfun(keyword_defs[i].chars); } JSTokenStream * js_NewTokenStream(JSContext *cx, const jschar *base, size_t length, const char *filename, uintN lineno, JSPrincipals *principals) { JSTokenStream *ts; ts = js_NewBufferTokenStream(cx, base, length); if (!ts) return NULL; ts->filename = filename; ts->lineno = lineno; if (principals) JSPRINCIPALS_HOLD(cx, principals); ts->principals = principals; return ts; } #define TBMIN 64 static JSBool GrowTokenBuf(JSStringBuffer *sb, size_t newlength) { JSContext *cx; jschar *base; ptrdiff_t offset, length; size_t tbsize; JSArenaPool *pool; cx = sb->data; base = sb->base; offset = PTRDIFF(sb->ptr, base, jschar); pool = &cx->tempPool; if (!base) { tbsize = TBMIN * sizeof(jschar); length = TBMIN - 1; JS_ARENA_ALLOCATE_CAST(base, jschar *, pool, tbsize); } else { length = PTRDIFF(sb->limit, base, jschar); if ((size_t)length >= ~(size_t)0 / sizeof(jschar)) { base = NULL; } else { tbsize = (length + 1) * sizeof(jschar); length += length + 1; JS_ARENA_GROW_CAST(base, jschar *, pool, tbsize, tbsize); } } if (!base) { JS_ReportOutOfMemory(cx); sb->base = STRING_BUFFER_ERROR_BASE; return JS_FALSE; } sb->base = base; sb->limit = base + length; sb->ptr = base + offset; return JS_TRUE; } JS_FRIEND_API(JSTokenStream *) js_NewBufferTokenStream(JSContext *cx, const jschar *base, size_t length) { size_t nb; JSTokenStream *ts; nb = sizeof(JSTokenStream) + JS_LINE_LIMIT * sizeof(jschar); JS_ARENA_ALLOCATE_CAST(ts, JSTokenStream *, &cx->tempPool, nb); if (!ts) { JS_ReportOutOfMemory(cx); return NULL; } memset(ts, 0, nb); ts->lineno = 1; ts->linebuf.base = ts->linebuf.limit = ts->linebuf.ptr = (jschar *)(ts + 1); ts->userbuf.base = (jschar *)base; ts->userbuf.limit = (jschar *)base + length; ts->userbuf.ptr = (jschar *)base; ts->tokenbuf.grow = GrowTokenBuf; ts->tokenbuf.data = cx; ts->listener = cx->runtime->sourceHandler; ts->listenerData = cx->runtime->sourceHandlerData; return ts; } JS_FRIEND_API(JSTokenStream *) js_NewFileTokenStream(JSContext *cx, const char *filename, FILE *defaultfp) { jschar *base; JSTokenStream *ts; FILE *file; JS_ARENA_ALLOCATE_CAST(base, jschar *, &cx->tempPool, JS_LINE_LIMIT * sizeof(jschar)); if (!base) return NULL; ts = js_NewBufferTokenStream(cx, base, JS_LINE_LIMIT); if (!ts) return NULL; if (!filename || strcmp(filename, "-") == 0) { file = defaultfp; } else { file = fopen(filename, "r"); if (!file) { JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_CANT_OPEN, filename, "No such file or directory"); return NULL; } } ts->userbuf.ptr = ts->userbuf.limit; ts->file = file; ts->filename = filename; return ts; } JS_FRIEND_API(JSBool) js_CloseTokenStream(JSContext *cx, JSTokenStream *ts) { if (ts->flags & TSF_OWNFILENAME) JS_free(cx, (void *) ts->filename); if (ts->principals) JSPRINCIPALS_DROP(cx, ts->principals); return !ts->file || fclose(ts->file) == 0; } JS_FRIEND_API(int) js_fgets(char *buf, int size, FILE *file) { int n, i, c; JSBool crflag; n = size - 1; if (n < 0) return -1; crflag = JS_FALSE; for (i = 0; i < n && (c = getc(file)) != EOF; i++) { buf[i] = c; if (c == '\n') { /* any \n ends a line */ i++; /* keep the \n; we know there is room for \0 */ break; } if (crflag) { /* \r not followed by \n ends line at the \r */ ungetc(c, file); break; /* and overwrite c in buf with \0 */ } crflag = (c == '\r'); } buf[i] = '\0'; return i; } static int32 GetChar(JSTokenStream *ts) { int32 c; ptrdiff_t i, j, len, olen; JSBool crflag; char cbuf[JS_LINE_LIMIT]; jschar *ubuf, *nl; if (ts->ungetpos != 0) { c = ts->ungetbuf[--ts->ungetpos]; } else { do { if (ts->linebuf.ptr == ts->linebuf.limit) { len = PTRDIFF(ts->userbuf.limit, ts->userbuf.ptr, jschar); if (len <= 0) { if (!ts->file) { ts->flags |= TSF_EOF; return EOF; } /* Fill ts->userbuf so that \r and \r\n convert to \n. */ crflag = (ts->flags & TSF_CRFLAG) != 0; len = js_fgets(cbuf, JS_LINE_LIMIT - crflag, ts->file); if (len <= 0) { ts->flags |= TSF_EOF; return EOF; } olen = len; ubuf = ts->userbuf.base; i = 0; if (crflag) { ts->flags &= ~TSF_CRFLAG; if (cbuf[0] != '\n') { ubuf[i++] = '\n'; len++; ts->linepos--; } } for (j = 0; i < len; i++, j++) ubuf[i] = (jschar) (unsigned char) cbuf[j]; ts->userbuf.limit = ubuf + len; ts->userbuf.ptr = ubuf; } if (ts->listener) { ts->listener(ts->filename, ts->lineno, ts->userbuf.ptr, len, &ts->listenerTSData, ts->listenerData); } nl = ts->saveEOL; if (!nl) { /* * Any one of \n, \r, or \r\n ends a line (the longest * match wins). Also allow the Unicode line and paragraph * separators. */ for (nl = ts->userbuf.ptr; nl < ts->userbuf.limit; nl++) { /* * Try to prevent value-testing on most characters by * filtering out characters that aren't 000x or 202x. */ if ((*nl & 0xDFD0) == 0) { if (*nl == '\n') break; if (*nl == '\r') { if (nl + 1 < ts->userbuf.limit && nl[1] == '\n') nl++; break; } if (*nl == LINE_SEPARATOR || *nl == PARA_SEPARATOR) break; } } } /* * If there was a line terminator, copy thru it into linebuf. * Else copy JS_LINE_LIMIT-1 bytes into linebuf. */ if (nl < ts->userbuf.limit) len = PTRDIFF(nl, ts->userbuf.ptr, jschar) + 1; if (len >= JS_LINE_LIMIT) { len = JS_LINE_LIMIT - 1; ts->saveEOL = nl; } else { ts->saveEOL = NULL; } js_strncpy(ts->linebuf.base, ts->userbuf.ptr, len); ts->userbuf.ptr += len; olen = len; /* * Make sure linebuf contains \n for EOL (don't do this in * userbuf because the user's string might be readonly). */ if (nl < ts->userbuf.limit) { if (*nl == '\r') { if (ts->linebuf.base[len-1] == '\r') { /* * Does the line segment end in \r? We must check * for a \n at the front of the next segment before * storing a \n into linebuf. This case matters * only when we're reading from a file. */ if (nl + 1 == ts->userbuf.limit && ts->file) { len--; ts->flags |= TSF_CRFLAG; /* clear NLFLAG? */ if (len == 0) { /* * This can happen when a segment ends in * \r\r. Start over. ptr == limit in this * case, so we'll fall into buffer-filling * code. */ return GetChar(ts); } } else { ts->linebuf.base[len-1] = '\n'; } } } else if (*nl == '\n') { if (nl > ts->userbuf.base && nl[-1] == '\r' && ts->linebuf.base[len-2] == '\r') { len--; JS_ASSERT(ts->linebuf.base[len] == '\n'); ts->linebuf.base[len-1] = '\n'; } } else if (*nl == LINE_SEPARATOR || *nl == PARA_SEPARATOR) { ts->linebuf.base[len-1] = '\n'; } } /* Reset linebuf based on adjusted segment length. */ ts->linebuf.limit = ts->linebuf.base + len; ts->linebuf.ptr = ts->linebuf.base; /* Update position of linebuf within physical userbuf line. */ if (!(ts->flags & TSF_NLFLAG)) ts->linepos += ts->linelen; else ts->linepos = 0; if (ts->linebuf.limit[-1] == '\n') ts->flags |= TSF_NLFLAG; else ts->flags &= ~TSF_NLFLAG; /* Update linelen from original segment length. */ ts->linelen = olen; } c = *ts->linebuf.ptr++; } while (JS_ISFORMAT(c)); } if (c == '\n') ts->lineno++; return c; } static void UngetChar(JSTokenStream *ts, int32 c) { if (c == EOF) return; JS_ASSERT(ts->ungetpos < sizeof ts->ungetbuf / sizeof ts->ungetbuf[0]); if (c == '\n') ts->lineno--; ts->ungetbuf[ts->ungetpos++] = (jschar)c; } static int32 PeekChar(JSTokenStream *ts) { int32 c; c = GetChar(ts); UngetChar(ts, c); return c; } /* * Peek n chars ahead into ts. Return true if n chars were read, false if * there weren't enough characters in the input stream. This function cannot * be used to peek into or past a newline. */ static JSBool PeekChars(JSTokenStream *ts, intN n, jschar *cp) { intN i, j; int32 c; for (i = 0; i < n; i++) { c = GetChar(ts); if (c == EOF) break; if (c == '\n') { UngetChar(ts, c); break; } cp[i] = (jschar)c; } for (j = i - 1; j >= 0; j--) UngetChar(ts, cp[j]); return i == n; } static void SkipChars(JSTokenStream *ts, intN n) { while (--n >= 0) GetChar(ts); } static JSBool MatchChar(JSTokenStream *ts, int32 expect) { int32 c; c = GetChar(ts); if (c == expect) return JS_TRUE; UngetChar(ts, c); return JS_FALSE; } static JSBool ReportCompileErrorNumber(JSContext *cx, void *handle, uintN flags, uintN errorNumber, JSErrorReport *report, JSBool charArgs, va_list ap) { JSTempValueRooter linetvr; JSString *linestr = NULL; JSTokenStream *ts = NULL; JSCodeGenerator *cg = NULL; JSParseNode *pn = NULL; JSErrorReporter onError; JSTokenPos *tp; JSStackFrame *fp; uintN index; char *message; JSBool warning; memset(report, 0, sizeof (struct JSErrorReport)); report->flags = flags; report->errorNumber = errorNumber; message = NULL; if (!js_ExpandErrorArguments(cx, js_GetErrorMessage, NULL, errorNumber, &message, report, &warning, charArgs, ap)) { return JS_FALSE; } JS_PUSH_TEMP_ROOT_STRING(cx, NULL, &linetvr); switch (flags & JSREPORT_HANDLE) { case JSREPORT_TS: ts = handle; break; case JSREPORT_CG: cg = handle; break; case JSREPORT_PN: pn = handle; ts = pn->pn_ts; break; } JS_ASSERT(!ts || ts->linebuf.limit < ts->linebuf.base + JS_LINE_LIMIT); /* * We are typically called with non-null ts and null cg from jsparse.c. * We can be called with null ts from the regexp compilation functions. * The code generator (jsemit.c) may pass null ts and non-null cg. */ do { if (ts) { report->filename = ts->filename; if (pn) { report->lineno = pn->pn_pos.begin.lineno; if (report->lineno != ts->lineno) break; } report->lineno = ts->lineno; linestr = js_NewStringCopyN(cx, ts->linebuf.base, PTRDIFF(ts->linebuf.limit, ts->linebuf.base, jschar), 0); linetvr.u.string = linestr; report->linebuf = linestr ? JS_GetStringBytes(linestr) : NULL; tp = &ts->tokens[(ts->cursor+ts->lookahead) & NTOKENS_MASK].pos; if (pn) tp = &pn->pn_pos; /* * FIXME: What should instead happen here is that we should * find error-tokens in userbuf, if !ts->file. That will * allow us to deliver a more helpful error message, which * includes all or part of the bad string or bad token. The * code here yields something that looks truncated. * See https://bugzilla.mozilla.org/show_bug.cgi?id=352970 */ index = 0; if (tp->begin.lineno == tp->end.lineno) { if (tp->begin.index < ts->linepos) break; index = tp->begin.index - ts->linepos; } report->tokenptr = linestr ? report->linebuf + index : NULL; report->uclinebuf = linestr ? JS_GetStringChars(linestr) : NULL; report->uctokenptr = linestr ? report->uclinebuf + index : NULL; break; } if (cg) { report->filename = cg->filename; report->lineno = CG_CURRENT_LINE(cg); break; } /* * If we can't find out where the error was based on the current * frame, see if the next frame has a script/pc combo we can use. */ for (fp = cx->fp; fp; fp = fp->down) { if (fp->script && fp->pc) { report->filename = fp->script->filename; report->lineno = js_PCToLineNumber(cx, fp->script, fp->pc); break; } } } while (0); /* * If there's a runtime exception type associated with this error * number, set that as the pending exception. For errors occuring at * compile time, this is very likely to be a JSEXN_SYNTAXERR. * * If an exception is thrown but not caught, the JSREPORT_EXCEPTION * flag will be set in report.flags. Proper behavior for an error * reporter is to ignore a report with this flag for all but top-level * compilation errors. The exception will remain pending, and so long * as the non-top-level "load", "eval", or "compile" native function * returns false, the top-level reporter will eventually receive the * uncaught exception report. * * XXX it'd probably be best if there was only one call to this * function, but there seem to be two error reporter call points. */ onError = cx->errorReporter; /* * Try to raise an exception only if there isn't one already set -- * otherwise the exception will describe the last compile-time error, * which is likely spurious. */ if (!ts || !(ts->flags & TSF_ERROR)) { if (js_ErrorToException(cx, message, report)) onError = NULL; } /* * Suppress any compile-time errors that don't occur at the top level. * This may still fail, as interplevel may be zero in contexts where we * don't really want to call the error reporter, as when js is called * by other code which could catch the error. */ if (cx->interpLevel != 0 && !JSREPORT_IS_WARNING(flags)) onError = NULL; if (onError) { JSDebugErrorHook hook = cx->runtime->debugErrorHook; /* * If debugErrorHook is present then we give it a chance to veto * sending the error on to the regular error reporter. */ if (hook && !hook(cx, message, report, cx->runtime->debugErrorHookData)) { onError = NULL; } } if (onError) (*onError)(cx, message, report); if (message) JS_free(cx, message); if (report->ucmessage) JS_free(cx, (void *)report->ucmessage); JS_POP_TEMP_ROOT(cx, &linetvr); if (ts && !JSREPORT_IS_WARNING(flags)) { /* Set the error flag to suppress spurious reports. */ ts->flags |= TSF_ERROR; } return warning; } JSBool js_ReportCompileErrorNumber(JSContext *cx, void *handle, uintN flags, uintN errorNumber, ...) { va_list ap; JSErrorReport report; JSBool warning; if ((flags & JSREPORT_STRICT) && !JS_HAS_STRICT_OPTION(cx)) return JS_TRUE; va_start(ap, errorNumber); warning = ReportCompileErrorNumber(cx, handle, flags, errorNumber, &report, JS_TRUE, ap); va_end(ap); /* * We have to do this here because js_ReportCompileErrorNumberUC doesn't * need to do this. */ if (report.messageArgs) { int i = 0; while (report.messageArgs[i]) JS_free(cx, (void *)report.messageArgs[i++]); JS_free(cx, (void *)report.messageArgs); } return warning; } JSBool js_ReportCompileErrorNumberUC(JSContext *cx, void *handle, uintN flags, uintN errorNumber, ...) { va_list ap; JSErrorReport report; JSBool warning; if ((flags & JSREPORT_STRICT) && !JS_HAS_STRICT_OPTION(cx)) return JS_TRUE; va_start(ap, errorNumber); warning = ReportCompileErrorNumber(cx, handle, flags, errorNumber, &report, JS_FALSE, ap); va_end(ap); if (report.messageArgs) JS_free(cx, (void *)report.messageArgs); return warning; } static JSBool GrowStringBuffer(JSStringBuffer *sb, size_t newlength) { ptrdiff_t offset; jschar *bp; offset = PTRDIFF(sb->ptr, sb->base, jschar); JS_ASSERT(offset >= 0); newlength += offset + 1; if ((size_t)offset < newlength && newlength < ~(size_t)0 / sizeof(jschar)) bp = realloc(sb->base, newlength * sizeof(jschar)); else bp = NULL; if (!bp) { free(sb->base); sb->base = STRING_BUFFER_ERROR_BASE; return JS_FALSE; } sb->base = bp; sb->ptr = bp + offset; sb->limit = bp + newlength - 1; return JS_TRUE; } static void FreeStringBuffer(JSStringBuffer *sb) { JS_ASSERT(STRING_BUFFER_OK(sb)); if (sb->base) free(sb->base); } void js_InitStringBuffer(JSStringBuffer *sb) { sb->base = sb->limit = sb->ptr = NULL; sb->data = NULL; sb->grow = GrowStringBuffer; sb->free = FreeStringBuffer; } void js_FinishStringBuffer(JSStringBuffer *sb) { sb->free(sb); } #define ENSURE_STRING_BUFFER(sb,n) \ ((sb)->ptr + (n) <= (sb)->limit || sb->grow(sb, n)) static void FastAppendChar(JSStringBuffer *sb, jschar c) { if (!STRING_BUFFER_OK(sb)) return; if (!ENSURE_STRING_BUFFER(sb, 1)) return; *sb->ptr++ = c; } void js_AppendChar(JSStringBuffer *sb, jschar c) { jschar *bp; if (!STRING_BUFFER_OK(sb)) return; if (!ENSURE_STRING_BUFFER(sb, 1)) return; bp = sb->ptr; *bp++ = c; *bp = 0; sb->ptr = bp; } #if JS_HAS_XML_SUPPORT void js_RepeatChar(JSStringBuffer *sb, jschar c, uintN count) { jschar *bp; if (!STRING_BUFFER_OK(sb) || count == 0) return; if (!ENSURE_STRING_BUFFER(sb, count)) return; for (bp = sb->ptr; count; --count) *bp++ = c; *bp = 0; sb->ptr = bp; } void js_AppendCString(JSStringBuffer *sb, const char *asciiz) { size_t length; jschar *bp; if (!STRING_BUFFER_OK(sb) || *asciiz == '\0') return; length = strlen(asciiz); if (!ENSURE_STRING_BUFFER(sb, length)) return; for (bp = sb->ptr; length; --length) *bp++ = (jschar) *asciiz++; *bp = 0; sb->ptr = bp; } void js_AppendJSString(JSStringBuffer *sb, JSString *str) { size_t length; jschar *bp; if (!STRING_BUFFER_OK(sb)) return; length = JSSTRING_LENGTH(str); if (length == 0 || !ENSURE_STRING_BUFFER(sb, length)) return; bp = sb->ptr; js_strncpy(bp, JSSTRING_CHARS(str), length); bp += length; *bp = 0; sb->ptr = bp; } static JSBool GetXMLEntity(JSContext *cx, JSTokenStream *ts) { ptrdiff_t offset, length, i; int32 c, d; JSBool ispair; jschar *bp, digit; char *bytes; JSErrNum msg; /* Put the entity, including the '&' already scanned, in ts->tokenbuf. */ offset = PTRDIFF(ts->tokenbuf.ptr, ts->tokenbuf.base, jschar); FastAppendChar(&ts->tokenbuf, '&'); while ((c = GetChar(ts)) != ';') { if (c == EOF || c == '\n') { js_ReportCompileErrorNumber(cx, ts, JSREPORT_TS | JSREPORT_ERROR, JSMSG_END_OF_XML_ENTITY); return JS_FALSE; } FastAppendChar(&ts->tokenbuf, (jschar) c); } /* Let length be the number of jschars after the '&', including the ';'. */ length = PTRDIFF(ts->tokenbuf.ptr, ts->tokenbuf.base, jschar) - offset; bp = ts->tokenbuf.base + offset; c = d = 0; ispair = JS_FALSE; if (length > 2 && bp[1] == '#') { /* Match a well-formed XML Character Reference. */ i = 2; if (length > 3 && JS_TOLOWER(bp[i]) == 'x') { if (length > 9) /* at most 6 hex digits allowed */ goto badncr; while (++i < length) { digit = bp[i]; if (!JS7_ISHEX(digit)) goto badncr; c = (c << 4) + JS7_UNHEX(digit); } } else { while (i < length) { digit = bp[i++]; if (!JS7_ISDEC(digit)) goto badncr; c = (c * 10) + JS7_UNDEC(digit); if (c < 0) goto badncr; } } if (0x10000 <= c && c <= 0x10FFFF) { /* Form a surrogate pair (c, d) -- c is the high surrogate. */ d = 0xDC00 + (c & 0x3FF); c = 0xD7C0 + (c >> 10); ispair = JS_TRUE; } else { /* Enforce the http://www.w3.org/TR/REC-xml/#wf-Legalchar WFC. */ if (c != 0x9 && c != 0xA && c != 0xD && !(0x20 <= c && c <= 0xD7FF) && !(0xE000 <= c && c <= 0xFFFD)) { goto badncr; } } } else { /* Try to match one of the five XML 1.0 predefined entities. */ switch (length) { case 3: if (bp[2] == 't') { if (bp[1] == 'l') c = '<'; else if (bp[1] == 'g') c = '>'; } break; case 4: if (bp[1] == 'a' && bp[2] == 'm' && bp[3] == 'p') c = '&'; break; case 5: if (bp[3] == 'o') { if (bp[1] == 'a' && bp[2] == 'p' && bp[4] == 's') c = '\''; else if (bp[1] == 'q' && bp[2] == 'u' && bp[4] == 't') c = '"'; } break; } if (c == 0) { msg = JSMSG_UNKNOWN_XML_ENTITY; goto bad; } } /* If we matched, retract ts->tokenbuf and store the entity's value. */ *bp++ = (jschar) c; if (ispair) *bp++ = (jschar) d; *bp = 0; ts->tokenbuf.ptr = bp; return JS_TRUE; badncr: msg = JSMSG_BAD_XML_NCR; bad: /* No match: throw a TypeError per ECMA-357 10.3.2.1 step 8(a). */ bytes = js_DeflateString(cx, bp + 1, PTRDIFF(ts->tokenbuf.ptr, bp, jschar) - 1); if (bytes) { js_ReportCompileErrorNumber(cx, ts, JSREPORT_TS | JSREPORT_ERROR, msg, bytes); JS_free(cx, bytes); } return JS_FALSE; } #endif /* JS_HAS_XML_SUPPORT */ JSTokenType js_PeekToken(JSContext *cx, JSTokenStream *ts) { JSTokenType tt; if (ts->lookahead != 0) { tt = ts->tokens[(ts->cursor + ts->lookahead) & NTOKENS_MASK].type; } else { tt = js_GetToken(cx, ts); js_UngetToken(ts); } return tt; } JSTokenType js_PeekTokenSameLine(JSContext *cx, JSTokenStream *ts) { JSTokenType tt; if (!ON_CURRENT_LINE(ts, CURRENT_TOKEN(ts).pos)) return TOK_EOL; ts->flags |= TSF_NEWLINES; tt = js_PeekToken(cx, ts); ts->flags &= ~TSF_NEWLINES; return tt; } /* * We have encountered a '\': check for a Unicode escape sequence after it, * returning the character code value if we found a Unicode escape sequence. * Otherwise, non-destructively return the original '\'. */ static int32 GetUnicodeEscape(JSTokenStream *ts) { jschar cp[5]; int32 c; if (PeekChars(ts, 5, cp) && cp[0] == 'u' && JS7_ISHEX(cp[1]) && JS7_ISHEX(cp[2]) && JS7_ISHEX(cp[3]) && JS7_ISHEX(cp[4])) { c = (((((JS7_UNHEX(cp[1]) << 4) + JS7_UNHEX(cp[2])) << 4) + JS7_UNHEX(cp[3])) << 4) + JS7_UNHEX(cp[4]); SkipChars(ts, 5); return c; } return '\\'; } static JSToken * NewToken(JSTokenStream *ts, ptrdiff_t adjust) { JSToken *tp; ts->cursor = (ts->cursor + 1) & NTOKENS_MASK; tp = &CURRENT_TOKEN(ts); tp->ptr = ts->linebuf.ptr + adjust; tp->pos.begin.index = ts->linepos + PTRDIFF(tp->ptr, ts->linebuf.base, jschar) - ts->ungetpos; tp->pos.begin.lineno = tp->pos.end.lineno = (uint16)ts->lineno; return tp; } JSTokenType js_GetToken(JSContext *cx, JSTokenStream *ts) { JSTokenType tt; int32 c, qc; JSToken *tp; JSAtom *atom; JSBool hadUnicodeEscape; const struct keyword *kw; #define INIT_TOKENBUF() (ts->tokenbuf.ptr = ts->tokenbuf.base) #define TOKENBUF_LENGTH() PTRDIFF(ts->tokenbuf.ptr, ts->tokenbuf.base, jschar) #define TOKENBUF_OK() STRING_BUFFER_OK(&ts->tokenbuf) #define TOKENBUF_TO_ATOM() (TOKENBUF_OK() \ ? js_AtomizeChars(cx, \ TOKENBUF_BASE(), \ TOKENBUF_LENGTH(), \ 0) \ : NULL) #define ADD_TO_TOKENBUF(c) FastAppendChar(&ts->tokenbuf, (jschar) (c)) /* The following 4 macros should only be used when TOKENBUF_OK() is true. */ #define TOKENBUF_BASE() (ts->tokenbuf.base) #define TOKENBUF_CHAR(i) (ts->tokenbuf.base[i]) #define TRIM_TOKENBUF(i) (ts->tokenbuf.ptr = ts->tokenbuf.base + i) #define NUL_TERM_TOKENBUF() (*ts->tokenbuf.ptr = 0) /* Check for a pushed-back token resulting from mismatching lookahead. */ while (ts->lookahead != 0) { JS_ASSERT(!(ts->flags & TSF_XMLTEXTMODE)); ts->lookahead--; ts->cursor = (ts->cursor + 1) & NTOKENS_MASK; tt = CURRENT_TOKEN(ts).type; if (tt != TOK_EOL || (ts->flags & TSF_NEWLINES)) return tt; } /* If there was a fatal error, keep returning TOK_ERROR. */ if (ts->flags & TSF_ERROR) return TOK_ERROR; #if JS_HAS_XML_SUPPORT if (ts->flags & TSF_XMLTEXTMODE) { tt = TOK_XMLSPACE; /* veto if non-space, return TOK_XMLTEXT */ tp = NewToken(ts, 0); INIT_TOKENBUF(); qc = (ts->flags & TSF_XMLONLYMODE) ? '<' : '{'; while ((c = GetChar(ts)) != qc && c != '<' && c != EOF) { if (c == '&' && qc == '<') { if (!GetXMLEntity(cx, ts)) goto error; tt = TOK_XMLTEXT; continue; } if (!JS_ISXMLSPACE(c)) tt = TOK_XMLTEXT; ADD_TO_TOKENBUF(c); } UngetChar(ts, c); if (TOKENBUF_LENGTH() == 0) { atom = NULL; } else { atom = TOKENBUF_TO_ATOM(); if (!atom) goto error; } tp->pos.end.lineno = (uint16)ts->lineno; tp->t_op = JSOP_STRING; tp->t_atom = atom; goto out; } if (ts->flags & TSF_XMLTAGMODE) { tp = NewToken(ts, 0); c = GetChar(ts); if (JS_ISXMLSPACE(c)) { do { c = GetChar(ts); } while (JS_ISXMLSPACE(c)); UngetChar(ts, c); tt = TOK_XMLSPACE; goto out; } if (c == EOF) { tt = TOK_EOF; goto out; } INIT_TOKENBUF(); if (JS_ISXMLNSSTART(c)) { JSBool sawColon = JS_FALSE; ADD_TO_TOKENBUF(c); while ((c = GetChar(ts)) != EOF && JS_ISXMLNAME(c)) { if (c == ':') { int nextc; if (sawColon || (nextc = PeekChar(ts), ((ts->flags & TSF_XMLONLYMODE) || nextc != '{') && !JS_ISXMLNAME(nextc))) { js_ReportCompileErrorNumber(cx, ts, JSREPORT_TS | JSREPORT_ERROR, JSMSG_BAD_XML_QNAME); goto error; } sawColon = JS_TRUE; } ADD_TO_TOKENBUF(c); } UngetChar(ts, c); atom = TOKENBUF_TO_ATOM(); if (!atom) goto error; tp->t_op = JSOP_STRING; tp->t_atom = atom; tt = TOK_XMLNAME; goto out; } switch (c) { case '{': if (ts->flags & TSF_XMLONLYMODE) goto bad_xml_char; tt = TOK_LC; goto out; case '=': tt = TOK_ASSIGN; goto out; case '"': case '\'': qc = c; while ((c = GetChar(ts)) != qc) { if (c == EOF) { js_ReportCompileErrorNumber(cx, ts, JSREPORT_TS | JSREPORT_ERROR, JSMSG_UNTERMINATED_STRING); goto error; } /* * XML attribute values are double-quoted when pretty-printed, * so escape " if it is expressed directly in a single-quoted * attribute value. */ if (c == '"' && !(ts->flags & TSF_XMLONLYMODE)) { JS_ASSERT(qc == '\''); js_AppendCString(&ts->tokenbuf, js_quot_entity_str); continue; } if (c == '&' && (ts->flags & TSF_XMLONLYMODE)) { if (!GetXMLEntity(cx, ts)) goto error; continue; } ADD_TO_TOKENBUF(c); } atom = TOKENBUF_TO_ATOM(); if (!atom) goto error; tp->pos.end.lineno = (uint16)ts->lineno; tp->t_op = JSOP_STRING; tp->t_atom = atom; tt = TOK_XMLATTR; goto out; case '>': tt = TOK_XMLTAGC; goto out; case '/': if (MatchChar(ts, '>')) { tt = TOK_XMLPTAGC; goto out; } /* FALL THROUGH */ bad_xml_char: default: js_ReportCompileErrorNumber(cx, ts, JSREPORT_TS | JSREPORT_ERROR, JSMSG_BAD_XML_CHARACTER); goto error; } /* NOTREACHED */ } #endif /* JS_HAS_XML_SUPPORT */ retry: do { c = GetChar(ts); if (c == '\n') { ts->flags &= ~TSF_DIRTYLINE; if (ts->flags & TSF_NEWLINES) break; } } while (JS_ISSPACE(c)); tp = NewToken(ts, -1); if (c == EOF) { tt = TOK_EOF; goto out; } hadUnicodeEscape = JS_FALSE; if (JS_ISIDSTART(c) || (c == '\\' && (c = GetUnicodeEscape(ts), hadUnicodeEscape = JS_ISIDSTART(c)))) { INIT_TOKENBUF(); for (;;) { ADD_TO_TOKENBUF(c); c = GetChar(ts); if (c == '\\') { c = GetUnicodeEscape(ts); if (!JS_ISIDENT(c)) break; hadUnicodeEscape = JS_TRUE; } else { if (!JS_ISIDENT(c)) break; } } UngetChar(ts, c); /* * Check for keywords unless we saw Unicode escape or parser asks * to ignore keywords. */ if (!hadUnicodeEscape && !(ts->flags & TSF_KEYWORD_IS_NAME) && TOKENBUF_OK() && (kw = FindKeyword(TOKENBUF_BASE(), TOKENBUF_LENGTH()))) { if (kw->tokentype == TOK_RESERVED) { if (!js_ReportCompileErrorNumber(cx, ts, JSREPORT_TS | JSREPORT_WARNING | JSREPORT_STRICT, JSMSG_RESERVED_ID, kw->chars)) { goto error; } } else if (kw->version <= JSVERSION_NUMBER(cx)) { tt = kw->tokentype; tp->t_op = (JSOp) kw->op; goto out; } } atom = TOKENBUF_TO_ATOM(); if (!atom) goto error; tp->t_op = JSOP_NAME; tp->t_atom = atom; tt = TOK_NAME; goto out; } if (JS7_ISDEC(c) || (c == '.' && JS7_ISDEC(PeekChar(ts)))) { jsint radix; const jschar *endptr; jsdouble dval; radix = 10; INIT_TOKENBUF(); if (c == '0') { ADD_TO_TOKENBUF(c); c = GetChar(ts); if (JS_TOLOWER(c) == 'x') { ADD_TO_TOKENBUF(c); c = GetChar(ts); radix = 16; } else if (JS7_ISDEC(c)) { radix = 8; } } while (JS7_ISHEX(c)) { if (radix < 16) { if (JS7_ISLET(c)) break; /* * We permit 08 and 09 as decimal numbers, which makes our * behaviour a superset of the ECMA numeric grammar. We might * not always be so permissive, so we warn about it. */ if (radix == 8 && c >= '8') { if (!js_ReportCompileErrorNumber(cx, ts, JSREPORT_TS | JSREPORT_WARNING, JSMSG_BAD_OCTAL, c == '8' ? "08" : "09")) { goto error; } radix = 10; } } ADD_TO_TOKENBUF(c); c = GetChar(ts); } if (radix == 10 && (c == '.' || JS_TOLOWER(c) == 'e')) { if (c == '.') { do { ADD_TO_TOKENBUF(c); c = GetChar(ts); } while (JS7_ISDEC(c)); } if (JS_TOLOWER(c) == 'e') { ADD_TO_TOKENBUF(c); c = GetChar(ts); if (c == '+' || c == '-') { ADD_TO_TOKENBUF(c); c = GetChar(ts); } if (!JS7_ISDEC(c)) { js_ReportCompileErrorNumber(cx, ts, JSREPORT_TS | JSREPORT_ERROR, JSMSG_MISSING_EXPONENT); goto error; } do { ADD_TO_TOKENBUF(c); c = GetChar(ts); } while (JS7_ISDEC(c)); } } /* Put back the next char and NUL-terminate tokenbuf for js_strto*. */ UngetChar(ts, c); ADD_TO_TOKENBUF(0); if (!TOKENBUF_OK()) goto error; if (radix == 10) { if (!js_strtod(cx, TOKENBUF_BASE(), &endptr, &dval)) { js_ReportCompileErrorNumber(cx, ts, JSREPORT_TS | JSREPORT_ERROR, JSMSG_OUT_OF_MEMORY); goto error; } } else { if (!js_strtointeger(cx, TOKENBUF_BASE(), &endptr, radix, &dval)) { js_ReportCompileErrorNumber(cx, ts, JSREPORT_TS | JSREPORT_ERROR, JSMSG_OUT_OF_MEMORY); goto error; } } tp->t_dval = dval; tt = TOK_NUMBER; goto out; } if (c == '"' || c == '\'') { qc = c; INIT_TOKENBUF(); while ((c = GetChar(ts)) != qc) { if (c == '\n' || c == EOF) { UngetChar(ts, c); js_ReportCompileErrorNumber(cx, ts, JSREPORT_TS | JSREPORT_ERROR, JSMSG_UNTERMINATED_STRING); goto error; } if (c == '\\') { switch (c = GetChar(ts)) { case 'b': c = '\b'; break; case 'f': c = '\f'; break; case 'n': c = '\n'; break; case 'r': c = '\r'; break; case 't': c = '\t'; break; case 'v': c = '\v'; break; default: if ('0' <= c && c < '8') { int32 val = JS7_UNDEC(c); c = PeekChar(ts); if ('0' <= c && c < '8') { val = 8 * val + JS7_UNDEC(c); GetChar(ts); c = PeekChar(ts); if ('0' <= c && c < '8') { int32 save = val; val = 8 * val + JS7_UNDEC(c); if (val <= 0377) GetChar(ts); else val = save; } } c = (jschar)val; } else if (c == 'u') { jschar cp[4]; if (PeekChars(ts, 4, cp) && JS7_ISHEX(cp[0]) && JS7_ISHEX(cp[1]) && JS7_ISHEX(cp[2]) && JS7_ISHEX(cp[3])) { c = (((((JS7_UNHEX(cp[0]) << 4) + JS7_UNHEX(cp[1])) << 4) + JS7_UNHEX(cp[2])) << 4) + JS7_UNHEX(cp[3]); SkipChars(ts, 4); } } else if (c == 'x') { jschar cp[2]; if (PeekChars(ts, 2, cp) && JS7_ISHEX(cp[0]) && JS7_ISHEX(cp[1])) { c = (JS7_UNHEX(cp[0]) << 4) + JS7_UNHEX(cp[1]); SkipChars(ts, 2); } } else if (c == '\n' && JS_VERSION_IS_ECMA(cx)) { /* ECMA follows C by removing escaped newlines. */ continue; } break; } } ADD_TO_TOKENBUF(c); } atom = TOKENBUF_TO_ATOM(); if (!atom) goto error; tp->pos.end.lineno = (uint16)ts->lineno; tp->t_op = JSOP_STRING; tp->t_atom = atom; tt = TOK_STRING; goto out; } switch (c) { case '\n': tt = TOK_EOL; goto eol_out; case ';': tt = TOK_SEMI; break; case '[': tt = TOK_LB; break; case ']': tt = TOK_RB; break; case '{': tt = TOK_LC; break; case '}': tt = TOK_RC; break; case '(': tt = TOK_LP; break; case ')': tt = TOK_RP; break; case ',': tt = TOK_COMMA; break; case '?': tt = TOK_HOOK; break; case '.': #if JS_HAS_XML_SUPPORT if (MatchChar(ts, c)) tt = TOK_DBLDOT; else #endif tt = TOK_DOT; break; case ':': #if JS_HAS_XML_SUPPORT if (MatchChar(ts, c)) { tt = TOK_DBLCOLON; break; } #endif /* * Default so compiler can modify to JSOP_GETTER if 'p getter: v' in an * object initializer, likewise for setter. */ tp->t_op = JSOP_NOP; tt = TOK_COLON; break; case '|': if (MatchChar(ts, c)) { tt = TOK_OR; } else if (MatchChar(ts, '=')) { tp->t_op = JSOP_BITOR; tt = TOK_ASSIGN; } else { tt = TOK_BITOR; } break; case '^': if (MatchChar(ts, '=')) { tp->t_op = JSOP_BITXOR; tt = TOK_ASSIGN; } else { tt = TOK_BITXOR; } break; case '&': if (MatchChar(ts, c)) { tt = TOK_AND; } else if (MatchChar(ts, '=')) { tp->t_op = JSOP_BITAND; tt = TOK_ASSIGN; } else { tt = TOK_BITAND; } break; case '=': if (MatchChar(ts, c)) { tp->t_op = MatchChar(ts, c) ? JSOP_NEW_EQ : (JSOp)cx->jsop_eq; tt = TOK_EQOP; } else { tp->t_op = JSOP_NOP; tt = TOK_ASSIGN; } break; case '!': if (MatchChar(ts, '=')) { tp->t_op = MatchChar(ts, '=') ? JSOP_NEW_NE : (JSOp)cx->jsop_ne; tt = TOK_EQOP; } else { tp->t_op = JSOP_NOT; tt = TOK_UNARYOP; } break; #if JS_HAS_XML_SUPPORT case '@': tt = TOK_AT; break; #endif case '<': #if JS_HAS_XML_SUPPORT /* * After much testing, it's clear that Postel's advice to protocol * designers ("be liberal in what you accept, and conservative in what * you send") invites a natural-law repercussion for JS as "protocol": * * "If you are liberal in what you accept, others will utterly fail to * be conservative in what they send." * * Which means you will get within every //-style comment unless we have to. So we set * TSF_IN_HTML_COMMENT when a either on a clean line, or * only if (ts->flags & TSF_IN_HTML_COMMENT), in a //-style comment. * * This still works as before given a malformed comment hiding hack such as: * * * * It does not cope with malformed comment hiding hacks where --> is hidden * by C-style comments, or on a dirty line. Such cases are already broken. */ #define TSF_IN_HTML_COMMENT 0x2000 /* Ignore keywords and return TOK_NAME instead to the parser. */ #define TSF_KEYWORD_IS_NAME 0x4000 /* Unicode separators that are treated as line terminators, in addition to \n, \r */ #define LINE_SEPARATOR 0x2028 #define PARA_SEPARATOR 0x2029 /* * Create a new token stream, either from an input buffer or from a file. * Return null on file-open or memory-allocation failure. * * NB: All of js_New{,Buffer,File}TokenStream() return a pointer to transient * memory in the current context's temp pool. This memory is deallocated via * JS_ARENA_RELEASE() after parsing is finished. */ extern JSTokenStream * js_NewTokenStream(JSContext *cx, const jschar *base, size_t length, const char *filename, uintN lineno, JSPrincipals *principals); extern JS_FRIEND_API(JSTokenStream *) js_NewBufferTokenStream(JSContext *cx, const jschar *base, size_t length); extern JS_FRIEND_API(JSTokenStream *) js_NewFileTokenStream(JSContext *cx, const char *filename, FILE *defaultfp); extern JS_FRIEND_API(JSBool) js_CloseTokenStream(JSContext *cx, JSTokenStream *ts); extern JS_FRIEND_API(int) js_fgets(char *buf, int size, FILE *file); /* * If the given char array forms JavaScript keyword, return corresponding * token. Otherwise return TOK_EOF. */ extern JSTokenType js_CheckKeyword(const jschar *chars, size_t length); #define js_IsKeyword(chars, length) \ (js_CheckKeyword(chars, length) != TOK_EOF) /* * Friend-exported API entry point to call a mapping function on each reserved * identifier in the scanner's keyword table. */ extern JS_FRIEND_API(void) js_MapKeywords(void (*mapfun)(const char *)); /* * Report a compile-time error by its number, using ts or cg to show context. * Return true for a warning, false for an error. */ extern JSBool js_ReportCompileErrorNumber(JSContext *cx, void *handle, uintN flags, uintN errorNumber, ...); extern JSBool js_ReportCompileErrorNumberUC(JSContext *cx, void *handle, uintN flags, uintN errorNumber, ...); /* Steal some JSREPORT_* bits (see jsapi.h) to tell handle's type. */ #define JSREPORT_HANDLE 0x300 #define JSREPORT_TS 0x000 #define JSREPORT_CG 0x100 #define JSREPORT_PN 0x200 /* * Look ahead one token and return its type. */ extern JSTokenType js_PeekToken(JSContext *cx, JSTokenStream *ts); extern JSTokenType js_PeekTokenSameLine(JSContext *cx, JSTokenStream *ts); /* * Get the next token from ts. */ extern JSTokenType js_GetToken(JSContext *cx, JSTokenStream *ts); /* * Push back the last scanned token onto ts. */ extern void js_UngetToken(JSTokenStream *ts); /* * Get the next token from ts if its type is tt. */ extern JSBool js_MatchToken(JSContext *cx, JSTokenStream *ts, JSTokenType tt); JS_END_EXTERN_C #endif /* jsscan_h___ */ pacparser-1.4.5/src/spidermonkey/js/src/jsscope.c000066400000000000000000001715251464010763600220400ustar00rootroot00000000000000/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- * vim: set ts=8 sw=4 et tw=78: * * ***** BEGIN LICENSE BLOCK ***** * Version: MPL 1.1/GPL 2.0/LGPL 2.1 * * The contents of this file are subject to the Mozilla Public License Version * 1.1 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * http://www.mozilla.org/MPL/ * * Software distributed under the License is distributed on an "AS IS" basis, * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License * for the specific language governing rights and limitations under the * License. * * The Original Code is Mozilla Communicator client code, released * March 31, 1998. * * The Initial Developer of the Original Code is * Netscape Communications Corporation. * Portions created by the Initial Developer are Copyright (C) 1998 * the Initial Developer. All Rights Reserved. * * Contributor(s): * * Alternatively, the contents of this file may be used under the terms of * either of the GNU General Public License Version 2 or later (the "GPL"), * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), * in which case the provisions of the GPL or the LGPL are applicable instead * of those above. If you wish to allow use of your version of this file only * under the terms of either the GPL or the LGPL, and not to allow others to * use your version of this file under the terms of the MPL, indicate your * decision by deleting the provisions above and replace them with the notice * and other provisions required by the GPL or the LGPL. If you do not delete * the provisions above, a recipient may use your version of this file under * the terms of any one of the MPL, the GPL or the LGPL. * * ***** END LICENSE BLOCK ***** */ /* * JS symbol tables. */ #include "jsstddef.h" #include #include #include "jstypes.h" #include "jsarena.h" #include "jsbit.h" #include "jsclist.h" #include "jsdhash.h" #include "jsutil.h" /* Added by JSIFY */ #include "jsapi.h" #include "jsatom.h" #include "jscntxt.h" #include "jsdbgapi.h" #include "jslock.h" #include "jsnum.h" #include "jsscope.h" #include "jsstr.h" JSScope * js_GetMutableScope(JSContext *cx, JSObject *obj) { JSScope *scope, *newscope; scope = OBJ_SCOPE(obj); JS_ASSERT(JS_IS_SCOPE_LOCKED(cx, scope)); if (scope->object == obj) return scope; newscope = js_NewScope(cx, 0, scope->map.ops, LOCKED_OBJ_GET_CLASS(obj), obj); if (!newscope) return NULL; JS_LOCK_SCOPE(cx, newscope); obj->map = js_HoldObjectMap(cx, &newscope->map); scope = (JSScope *) js_DropObjectMap(cx, &scope->map, obj); JS_TRANSFER_SCOPE_LOCK(cx, scope, newscope); return newscope; } /* * JSScope uses multiplicative hashing, _a la_ jsdhash.[ch], but specialized * to minimize footprint. But if a scope has fewer than SCOPE_HASH_THRESHOLD * entries, we use linear search and avoid allocating scope->table. */ #define SCOPE_HASH_THRESHOLD 6 #define MIN_SCOPE_SIZE_LOG2 4 #define MIN_SCOPE_SIZE JS_BIT(MIN_SCOPE_SIZE_LOG2) #define SCOPE_TABLE_NBYTES(n) ((n) * sizeof(JSScopeProperty *)) static void InitMinimalScope(JSScope *scope) { scope->hashShift = JS_DHASH_BITS - MIN_SCOPE_SIZE_LOG2; scope->entryCount = scope->removedCount = 0; scope->table = NULL; scope->lastProp = NULL; } static JSBool CreateScopeTable(JSContext *cx, JSScope *scope, JSBool report) { int sizeLog2; JSScopeProperty *sprop, **spp; JS_ASSERT(!scope->table); JS_ASSERT(scope->lastProp); if (scope->entryCount > SCOPE_HASH_THRESHOLD) { /* * Ouch: calloc failed at least once already -- let's try again, * overallocating to hold at least twice the current population. */ sizeLog2 = JS_CeilingLog2(2 * scope->entryCount); scope->hashShift = JS_DHASH_BITS - sizeLog2; } else { JS_ASSERT(scope->hashShift == JS_DHASH_BITS - MIN_SCOPE_SIZE_LOG2); sizeLog2 = MIN_SCOPE_SIZE_LOG2; } scope->table = (JSScopeProperty **) calloc(JS_BIT(sizeLog2), sizeof(JSScopeProperty *)); if (!scope->table) { if (report) JS_ReportOutOfMemory(cx); return JS_FALSE; } js_UpdateMallocCounter(cx, JS_BIT(sizeLog2) * sizeof(JSScopeProperty *)); scope->hashShift = JS_DHASH_BITS - sizeLog2; for (sprop = scope->lastProp; sprop; sprop = sprop->parent) { spp = js_SearchScope(scope, sprop->id, JS_TRUE); SPROP_STORE_PRESERVING_COLLISION(spp, sprop); } return JS_TRUE; } JSScope * js_NewScope(JSContext *cx, jsrefcount nrefs, JSObjectOps *ops, JSClass *clasp, JSObject *obj) { JSScope *scope; scope = (JSScope *) JS_malloc(cx, sizeof(JSScope)); if (!scope) return NULL; js_InitObjectMap(&scope->map, nrefs, ops, clasp); scope->object = obj; scope->flags = 0; InitMinimalScope(scope); #ifdef JS_THREADSAFE scope->ownercx = cx; memset(&scope->lock, 0, sizeof scope->lock); /* * Set u.link = NULL, not u.count = 0, in case the target architecture's * null pointer has a non-zero integer representation. */ scope->u.link = NULL; #ifdef DEBUG scope->file[0] = scope->file[1] = scope->file[2] = scope->file[3] = NULL; scope->line[0] = scope->line[1] = scope->line[2] = scope->line[3] = 0; #endif #endif JS_RUNTIME_METER(cx->runtime, liveScopes); JS_RUNTIME_METER(cx->runtime, totalScopes); return scope; } #ifdef DEBUG_SCOPE_COUNT extern void js_unlog_scope(JSScope *scope); #endif void js_DestroyScope(JSContext *cx, JSScope *scope) { #ifdef DEBUG_SCOPE_COUNT js_unlog_scope(scope); #endif #ifdef JS_THREADSAFE /* Scope must be single-threaded at this point, so set scope->ownercx. */ JS_ASSERT(scope->u.count == 0); scope->ownercx = cx; js_FinishLock(&scope->lock); #endif if (scope->table) JS_free(cx, scope->table); #ifdef DEBUG JS_LOCK_RUNTIME_VOID(cx->runtime, cx->runtime->liveScopeProps -= scope->entryCount); #endif JS_RUNTIME_UNMETER(cx->runtime, liveScopes); JS_free(cx, scope); } #ifdef DUMP_SCOPE_STATS typedef struct JSScopeStats { jsrefcount searches; jsrefcount steps; jsrefcount hits; jsrefcount misses; jsrefcount stepHits; jsrefcount stepMisses; jsrefcount adds; jsrefcount redundantAdds; jsrefcount addFailures; jsrefcount changeFailures; jsrefcount compresses; jsrefcount grows; jsrefcount removes; jsrefcount removeFrees; jsrefcount uselessRemoves; jsrefcount shrinks; } JSScopeStats; JS_FRIEND_DATA(JSScopeStats) js_scope_stats; # define METER(x) JS_ATOMIC_INCREMENT(&js_scope_stats.x) #else # define METER(x) /* nothing */ #endif /* * Double hashing needs the second hash code to be relatively prime to table * size, so we simply make hash2 odd. The inputs to multiplicative hash are * the golden ratio, expressed as a fixed-point 32 bit fraction, and the int * property index or named property's atom number (observe that most objects * have either no indexed properties, or almost all indexed and a few names, * so collisions between index and atom number are unlikely). */ #define SCOPE_HASH0(id) (HASH_ID(id) * JS_GOLDEN_RATIO) #define SCOPE_HASH1(hash0,shift) ((hash0) >> (shift)) #define SCOPE_HASH2(hash0,log2,shift) ((((hash0) << (log2)) >> (shift)) | 1) JS_FRIEND_API(JSScopeProperty **) js_SearchScope(JSScope *scope, jsid id, JSBool adding) { JSHashNumber hash0, hash1, hash2; int hashShift, sizeLog2; JSScopeProperty *stored, *sprop, **spp, **firstRemoved; uint32 sizeMask; METER(searches); if (!scope->table) { /* Not enough properties to justify hashing: search from lastProp. */ JS_ASSERT(!SCOPE_HAD_MIDDLE_DELETE(scope)); for (spp = &scope->lastProp; (sprop = *spp); spp = &sprop->parent) { if (sprop->id == id) { METER(hits); return spp; } } METER(misses); return spp; } /* Compute the primary hash address. */ hash0 = SCOPE_HASH0(id); hashShift = scope->hashShift; hash1 = SCOPE_HASH1(hash0, hashShift); spp = scope->table + hash1; /* Miss: return space for a new entry. */ stored = *spp; if (SPROP_IS_FREE(stored)) { METER(misses); return spp; } /* Hit: return entry. */ sprop = SPROP_CLEAR_COLLISION(stored); if (sprop && sprop->id == id) { METER(hits); return spp; } /* Collision: double hash. */ sizeLog2 = JS_DHASH_BITS - hashShift; hash2 = SCOPE_HASH2(hash0, sizeLog2, hashShift); sizeMask = JS_BITMASK(sizeLog2); /* Save the first removed entry pointer so we can recycle it if adding. */ if (SPROP_IS_REMOVED(stored)) { firstRemoved = spp; } else { firstRemoved = NULL; if (adding && !SPROP_HAD_COLLISION(stored)) SPROP_FLAG_COLLISION(spp, sprop); } for (;;) { METER(steps); hash1 -= hash2; hash1 &= sizeMask; spp = scope->table + hash1; stored = *spp; if (SPROP_IS_FREE(stored)) { METER(stepMisses); return (adding && firstRemoved) ? firstRemoved : spp; } sprop = SPROP_CLEAR_COLLISION(stored); if (sprop && sprop->id == id) { METER(stepHits); return spp; } if (SPROP_IS_REMOVED(stored)) { if (!firstRemoved) firstRemoved = spp; } else { if (adding && !SPROP_HAD_COLLISION(stored)) SPROP_FLAG_COLLISION(spp, sprop); } } /* NOTREACHED */ return NULL; } static JSBool ChangeScope(JSContext *cx, JSScope *scope, int change) { int oldlog2, newlog2; uint32 oldsize, newsize, nbytes; JSScopeProperty **table, **oldtable, **spp, **oldspp, *sprop; /* Grow, shrink, or compress by changing scope->table. */ oldlog2 = JS_DHASH_BITS - scope->hashShift; newlog2 = oldlog2 + change; oldsize = JS_BIT(oldlog2); newsize = JS_BIT(newlog2); nbytes = SCOPE_TABLE_NBYTES(newsize); table = (JSScopeProperty **) calloc(nbytes, 1); if (!table) { JS_ReportOutOfMemory(cx); return JS_FALSE; } /* Now that we have a new table allocated, update scope members. */ scope->hashShift = JS_DHASH_BITS - newlog2; scope->removedCount = 0; oldtable = scope->table; scope->table = table; /* Treat the above calloc as a JS_malloc, to match CreateScopeTable. */ cx->runtime->gcMallocBytes += nbytes; /* Copy only live entries, leaving removed and free ones behind. */ for (oldspp = oldtable; oldsize != 0; oldspp++) { sprop = SPROP_FETCH(oldspp); if (sprop) { spp = js_SearchScope(scope, sprop->id, JS_TRUE); JS_ASSERT(SPROP_IS_FREE(*spp)); *spp = sprop; } oldsize--; } /* Finally, free the old table storage. */ JS_free(cx, oldtable); return JS_TRUE; } /* * Take care to exclude the mark and duplicate bits, in case we're called from * the GC, or we are searching for a property that has not yet been flagged as * a duplicate when making a duplicate formal parameter. */ #define SPROP_FLAGS_NOT_MATCHED (SPROP_MARK | SPROP_IS_DUPLICATE) JS_STATIC_DLL_CALLBACK(JSDHashNumber) js_HashScopeProperty(JSDHashTable *table, const void *key) { const JSScopeProperty *sprop = (const JSScopeProperty *)key; JSDHashNumber hash; JSPropertyOp gsop; /* Accumulate from least to most random so the low bits are most random. */ hash = 0; gsop = sprop->getter; if (gsop) hash = (hash >> (JS_DHASH_BITS - 4)) ^ (hash << 4) ^ (jsword)gsop; gsop = sprop->setter; if (gsop) hash = (hash >> (JS_DHASH_BITS - 4)) ^ (hash << 4) ^ (jsword)gsop; hash = (hash >> (JS_DHASH_BITS - 4)) ^ (hash << 4) ^ (sprop->flags & ~SPROP_FLAGS_NOT_MATCHED); hash = (hash >> (JS_DHASH_BITS - 4)) ^ (hash << 4) ^ sprop->attrs; hash = (hash >> (JS_DHASH_BITS - 4)) ^ (hash << 4) ^ sprop->shortid; hash = (hash >> (JS_DHASH_BITS - 4)) ^ (hash << 4) ^ sprop->slot; hash = (hash >> (JS_DHASH_BITS - 4)) ^ (hash << 4) ^ sprop->id; return hash; } #define SPROP_MATCH(sprop, child) \ SPROP_MATCH_PARAMS(sprop, (child)->id, (child)->getter, (child)->setter, \ (child)->slot, (child)->attrs, (child)->flags, \ (child)->shortid) #define SPROP_MATCH_PARAMS(sprop, aid, agetter, asetter, aslot, aattrs, \ aflags, ashortid) \ ((sprop)->id == (aid) && \ SPROP_MATCH_PARAMS_AFTER_ID(sprop, agetter, asetter, aslot, aattrs, \ aflags, ashortid)) #define SPROP_MATCH_PARAMS_AFTER_ID(sprop, agetter, asetter, aslot, aattrs, \ aflags, ashortid) \ ((sprop)->getter == (agetter) && \ (sprop)->setter == (asetter) && \ (sprop)->slot == (aslot) && \ (sprop)->attrs == (aattrs) && \ (((sprop)->flags ^ (aflags)) & ~SPROP_FLAGS_NOT_MATCHED) == 0 && \ (sprop)->shortid == (ashortid)) JS_STATIC_DLL_CALLBACK(JSBool) js_MatchScopeProperty(JSDHashTable *table, const JSDHashEntryHdr *hdr, const void *key) { const JSPropertyTreeEntry *entry = (const JSPropertyTreeEntry *)hdr; const JSScopeProperty *sprop = entry->child; const JSScopeProperty *kprop = (const JSScopeProperty *)key; return SPROP_MATCH(sprop, kprop); } static const JSDHashTableOps PropertyTreeHashOps = { JS_DHashAllocTable, JS_DHashFreeTable, JS_DHashGetKeyStub, js_HashScopeProperty, js_MatchScopeProperty, JS_DHashMoveEntryStub, JS_DHashClearEntryStub, JS_DHashFinalizeStub, NULL }; /* * A property tree node on rt->propertyFreeList overlays the following prefix * struct on JSScopeProperty. */ typedef struct FreeNode { jsid id; JSScopeProperty *next; JSScopeProperty **prevp; } FreeNode; #define FREENODE(sprop) ((FreeNode *) (sprop)) #define FREENODE_INSERT(list, sprop) \ JS_BEGIN_MACRO \ FREENODE(sprop)->next = (list); \ FREENODE(sprop)->prevp = &(list); \ if (list) \ FREENODE(list)->prevp = &FREENODE(sprop)->next; \ (list) = (sprop); \ JS_END_MACRO #define FREENODE_REMOVE(sprop) \ JS_BEGIN_MACRO \ *FREENODE(sprop)->prevp = FREENODE(sprop)->next; \ if (FREENODE(sprop)->next) \ FREENODE(FREENODE(sprop)->next)->prevp = FREENODE(sprop)->prevp; \ JS_END_MACRO /* NB: Called with the runtime lock held. */ static JSScopeProperty * NewScopeProperty(JSRuntime *rt) { JSScopeProperty *sprop; sprop = rt->propertyFreeList; if (sprop) { FREENODE_REMOVE(sprop); } else { JS_ARENA_ALLOCATE_CAST(sprop, JSScopeProperty *, &rt->propertyArenaPool, sizeof(JSScopeProperty)); if (!sprop) return NULL; } JS_RUNTIME_METER(rt, livePropTreeNodes); JS_RUNTIME_METER(rt, totalPropTreeNodes); return sprop; } #define CHUNKY_KIDS_TAG ((jsuword)1) #define KIDS_IS_CHUNKY(kids) ((jsuword)(kids) & CHUNKY_KIDS_TAG) #define KIDS_TO_CHUNK(kids) ((PropTreeKidsChunk *) \ ((jsuword)(kids) & ~CHUNKY_KIDS_TAG)) #define CHUNK_TO_KIDS(chunk) ((JSScopeProperty *) \ ((jsuword)(chunk) | CHUNKY_KIDS_TAG)) #define MAX_KIDS_PER_CHUNK 10 typedef struct PropTreeKidsChunk PropTreeKidsChunk; struct PropTreeKidsChunk { JSScopeProperty *kids[MAX_KIDS_PER_CHUNK]; PropTreeKidsChunk *next; }; static PropTreeKidsChunk * NewPropTreeKidsChunk(JSRuntime *rt) { PropTreeKidsChunk *chunk; chunk = calloc(1, sizeof *chunk); if (!chunk) return NULL; JS_ASSERT(((jsuword)chunk & CHUNKY_KIDS_TAG) == 0); JS_RUNTIME_METER(rt, propTreeKidsChunks); return chunk; } static void DestroyPropTreeKidsChunk(JSRuntime *rt, PropTreeKidsChunk *chunk) { JS_RUNTIME_UNMETER(rt, propTreeKidsChunks); free(chunk); } /* NB: Called with the runtime lock held. */ static JSBool InsertPropertyTreeChild(JSRuntime *rt, JSScopeProperty *parent, JSScopeProperty *child, PropTreeKidsChunk *sweptChunk) { JSPropertyTreeEntry *entry; JSScopeProperty **childp, *kids, *sprop; PropTreeKidsChunk *chunk, **chunkp; uintN i; JS_ASSERT(!parent || child->parent != parent); if (!parent) { entry = (JSPropertyTreeEntry *) JS_DHashTableOperate(&rt->propertyTreeHash, child, JS_DHASH_ADD); if (!entry) return JS_FALSE; childp = &entry->child; sprop = *childp; if (!sprop) { *childp = child; } else { /* * A "Duplicate child" case. * * We can't do away with child, as at least one live scope entry * still points at it. What's more, that scope's lastProp chains * through an ancestor line to reach child, and js_Enumerate and * others count on this linkage. We must leave child out of the * hash table, and not require it to be there when we eventually * GC it (see RemovePropertyTreeChild, below). * * It is necessary to leave the duplicate child out of the hash * table to preserve entry uniqueness. It is safe to leave the * child out of the hash table (unlike the duplicate child cases * below), because the child's parent link will be null, which * can't dangle. */ JS_ASSERT(sprop != child && SPROP_MATCH(sprop, child)); JS_RUNTIME_METER(rt, duplicatePropTreeNodes); } } else { childp = &parent->kids; kids = *childp; if (kids) { if (KIDS_IS_CHUNKY(kids)) { chunk = KIDS_TO_CHUNK(kids); do { for (i = 0; i < MAX_KIDS_PER_CHUNK; i++) { childp = &chunk->kids[i]; sprop = *childp; if (!sprop) goto insert; JS_ASSERT(sprop != child); if (SPROP_MATCH(sprop, child)) { /* * Duplicate child, see comment above. In this * case, we must let the duplicate be inserted at * this level in the tree, so we keep iterating, * looking for an empty slot in which to insert. */ JS_ASSERT(sprop != child); JS_RUNTIME_METER(rt, duplicatePropTreeNodes); } } chunkp = &chunk->next; } while ((chunk = *chunkp) != NULL); if (sweptChunk) { chunk = sweptChunk; } else { chunk = NewPropTreeKidsChunk(rt); if (!chunk) return JS_FALSE; } *chunkp = chunk; childp = &chunk->kids[0]; } else { sprop = kids; JS_ASSERT(sprop != child); if (SPROP_MATCH(sprop, child)) { /* * Duplicate child, see comment above. Once again, we * must let duplicates created by deletion pile up in a * kids-chunk-list, in order to find them when sweeping * and thereby avoid dangling parent pointers. */ JS_RUNTIME_METER(rt, duplicatePropTreeNodes); } if (sweptChunk) { chunk = sweptChunk; } else { chunk = NewPropTreeKidsChunk(rt); if (!chunk) return JS_FALSE; } parent->kids = CHUNK_TO_KIDS(chunk); chunk->kids[0] = sprop; childp = &chunk->kids[1]; } } insert: *childp = child; } child->parent = parent; return JS_TRUE; } /* NB: Called with the runtime lock held. */ static PropTreeKidsChunk * RemovePropertyTreeChild(JSRuntime *rt, JSScopeProperty *child) { JSPropertyTreeEntry *entry; JSScopeProperty *parent, *kids, *kid; PropTreeKidsChunk *list, *chunk, **chunkp, *lastChunk; uintN i, j; parent = child->parent; if (!parent) { /* * Don't remove child if it is not in rt->propertyTreeHash, but only * matches a root child in the table that has compatible members. See * the "Duplicate child" comments in InsertPropertyTreeChild, above. */ entry = (JSPropertyTreeEntry *) JS_DHashTableOperate(&rt->propertyTreeHash, child, JS_DHASH_LOOKUP); if (entry->child == child) JS_DHashTableRawRemove(&rt->propertyTreeHash, &entry->hdr); } else { kids = parent->kids; if (KIDS_IS_CHUNKY(kids)) { list = chunk = KIDS_TO_CHUNK(kids); chunkp = &list; do { for (i = 0; i < MAX_KIDS_PER_CHUNK; i++) { if (chunk->kids[i] == child) { lastChunk = chunk; if (!lastChunk->next) { j = i + 1; } else { j = 0; do { chunkp = &lastChunk->next; lastChunk = *chunkp; } while (lastChunk->next); } for (; j < MAX_KIDS_PER_CHUNK; j++) { if (!lastChunk->kids[j]) break; } --j; if (chunk != lastChunk || j > i) chunk->kids[i] = lastChunk->kids[j]; lastChunk->kids[j] = NULL; if (j == 0) { *chunkp = NULL; if (!list) parent->kids = NULL; return lastChunk; } return NULL; } } chunkp = &chunk->next; } while ((chunk = *chunkp) != NULL); } else { kid = kids; if (kid == child) parent->kids = NULL; } } return NULL; } /* * Called *without* the runtime lock held, this function acquires that lock * only when inserting a new child. Thus there may be races to find or add * a node that result in duplicates. We expect such races to be rare! */ static JSScopeProperty * GetPropertyTreeChild(JSContext *cx, JSScopeProperty *parent, JSScopeProperty *child) { JSRuntime *rt; JSPropertyTreeEntry *entry; JSScopeProperty *sprop; PropTreeKidsChunk *chunk; uintN i; rt = cx->runtime; if (!parent) { JS_LOCK_RUNTIME(rt); entry = (JSPropertyTreeEntry *) JS_DHashTableOperate(&rt->propertyTreeHash, child, JS_DHASH_ADD); if (!entry) goto out_of_memory; sprop = entry->child; if (sprop) goto out; } else { /* * Because chunks are appended at the end and never deleted except by * the GC, we can search without taking the runtime lock. We may miss * a matching sprop added by another thread, and make a duplicate one, * but that is an unlikely, therefore small, cost. The property tree * has extremely low fan-out below its root in popular embeddings with * real-world workloads. * * If workload changes so as to increase fan-out significantly below * the property tree root, we'll want to add another tag bit stored in * parent->kids that indicates a JSDHashTable pointer. */ entry = NULL; sprop = parent->kids; if (sprop) { if (KIDS_IS_CHUNKY(sprop)) { chunk = KIDS_TO_CHUNK(sprop); do { for (i = 0; i < MAX_KIDS_PER_CHUNK; i++) { sprop = chunk->kids[i]; if (!sprop) goto not_found; if (SPROP_MATCH(sprop, child)) return sprop; } } while ((chunk = chunk->next) != NULL); } else { if (SPROP_MATCH(sprop, child)) return sprop; } } not_found: JS_LOCK_RUNTIME(rt); } sprop = NewScopeProperty(rt); if (!sprop) goto out_of_memory; sprop->id = child->id; sprop->getter = child->getter; sprop->setter = child->setter; sprop->slot = child->slot; sprop->attrs = child->attrs; sprop->flags = child->flags; sprop->shortid = child->shortid; sprop->parent = sprop->kids = NULL; if (!parent) { entry->child = sprop; } else { if (!InsertPropertyTreeChild(rt, parent, sprop, NULL)) goto out_of_memory; } out: JS_UNLOCK_RUNTIME(rt); return sprop; out_of_memory: JS_UNLOCK_RUNTIME(rt); JS_ReportOutOfMemory(cx); return NULL; } #ifdef DEBUG_notbrendan #define CHECK_ANCESTOR_LINE(scope, sparse) \ JS_BEGIN_MACRO \ if ((scope)->table) CheckAncestorLine(scope, sparse); \ JS_END_MACRO static void CheckAncestorLine(JSScope *scope, JSBool sparse) { uint32 size; JSScopeProperty **spp, **start, **end, *ancestorLine, *sprop, *aprop; uint32 entryCount, ancestorCount; ancestorLine = SCOPE_LAST_PROP(scope); if (ancestorLine) JS_ASSERT(SCOPE_HAS_PROPERTY(scope, ancestorLine)); entryCount = 0; size = SCOPE_CAPACITY(scope); start = scope->table; for (spp = start, end = start + size; spp < end; spp++) { sprop = SPROP_FETCH(spp); if (sprop) { entryCount++; for (aprop = ancestorLine; aprop; aprop = aprop->parent) { if (aprop == sprop) break; } JS_ASSERT(aprop); } } JS_ASSERT(entryCount == scope->entryCount); ancestorCount = 0; for (sprop = ancestorLine; sprop; sprop = sprop->parent) { if (SCOPE_HAD_MIDDLE_DELETE(scope) && !SCOPE_HAS_PROPERTY(scope, sprop)) { JS_ASSERT(sparse || (sprop->flags & SPROP_IS_DUPLICATE)); continue; } ancestorCount++; } JS_ASSERT(ancestorCount == scope->entryCount); } #else #define CHECK_ANCESTOR_LINE(scope, sparse) /* nothing */ #endif static void ReportReadOnlyScope(JSContext *cx, JSScope *scope) { JSString *str; str = js_ValueToString(cx, OBJECT_TO_JSVAL(scope->object)); JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_READ_ONLY, str ? JS_GetStringBytes(str) : LOCKED_OBJ_GET_CLASS(scope->object)->name); } JSScopeProperty * js_AddScopeProperty(JSContext *cx, JSScope *scope, jsid id, JSPropertyOp getter, JSPropertyOp setter, uint32 slot, uintN attrs, uintN flags, intN shortid) { JSScopeProperty **spp, *sprop, *overwriting, **spvec, **spp2, child; uint32 size, splen, i; int change; JSTempValueRooter tvr; JS_ASSERT(JS_IS_SCOPE_LOCKED(cx, scope)); CHECK_ANCESTOR_LINE(scope, JS_TRUE); /* * You can't add properties to a sealed scope. But note well that you can * change property attributes in a sealed scope, even though that replaces * a JSScopeProperty * in the scope's hash table -- but no id is added, so * the scope remains sealed. */ if (SCOPE_IS_SEALED(scope)) { ReportReadOnlyScope(cx, scope); return NULL; } /* * Normalize stub getter and setter values for faster is-stub testing in * the SPROP_CALL_[GS]ETTER macros. */ if (getter == JS_PropertyStub) getter = NULL; if (setter == JS_PropertyStub) setter = NULL; /* * Search for id in order to claim its entry, allocating a property tree * node if one doesn't already exist for our parameters. */ spp = js_SearchScope(scope, id, JS_TRUE); sprop = overwriting = SPROP_FETCH(spp); if (!sprop) { /* Check whether we need to grow, if the load factor is >= .75. */ size = SCOPE_CAPACITY(scope); if (scope->entryCount + scope->removedCount >= size - (size >> 2)) { if (scope->removedCount >= size >> 2) { METER(compresses); change = 0; } else { METER(grows); change = 1; } if (!ChangeScope(cx, scope, change) && scope->entryCount + scope->removedCount == size - 1) { METER(addFailures); return NULL; } spp = js_SearchScope(scope, id, JS_TRUE); JS_ASSERT(!SPROP_FETCH(spp)); } } else { /* Property exists: js_SearchScope must have returned a valid entry. */ JS_ASSERT(!SPROP_IS_REMOVED(*spp)); /* * If all property members match, this is a redundant add and we can * return early. If the caller wants to allocate a slot, but doesn't * care which slot, copy sprop->slot into slot so we can match sprop, * if all other members match. */ if (!(attrs & JSPROP_SHARED) && slot == SPROP_INVALID_SLOT && SPROP_HAS_VALID_SLOT(sprop, scope)) { slot = sprop->slot; } if (SPROP_MATCH_PARAMS_AFTER_ID(sprop, getter, setter, slot, attrs, flags, shortid)) { METER(redundantAdds); return sprop; } /* * Duplicate formal parameters require us to leave the old property * on the ancestor line, so the decompiler can find it, even though * its entry in scope->table is overwritten to point at a new property * descending from the old one. The SPROP_IS_DUPLICATE flag helps us * cope with the consequent disparity between ancestor line height and * scope->entryCount. */ if (flags & SPROP_IS_DUPLICATE) { sprop->flags |= SPROP_IS_DUPLICATE; } else { /* * If we are clearing sprop to force an existing property to be * overwritten (apart from a duplicate formal parameter), we must * unlink it from the ancestor line at scope->lastProp, lazily if * sprop is not lastProp. And we must remove the entry at *spp, * precisely so the lazy "middle delete" fixup code further below * won't find sprop in scope->table, in spite of sprop being on * the ancestor line. * * When we finally succeed in finding or creating a new sprop * and storing its pointer at *spp, we'll use the |overwriting| * local saved when we first looked up id to decide whether we're * indeed creating a new entry, or merely overwriting an existing * property. */ if (sprop == SCOPE_LAST_PROP(scope)) { do { SCOPE_REMOVE_LAST_PROP(scope); if (!SCOPE_HAD_MIDDLE_DELETE(scope)) break; sprop = SCOPE_LAST_PROP(scope); } while (sprop && !SCOPE_HAS_PROPERTY(scope, sprop)); } else if (!SCOPE_HAD_MIDDLE_DELETE(scope)) { /* * If we have no hash table yet, we need one now. The middle * delete code is simple-minded that way! */ if (!scope->table) { if (!CreateScopeTable(cx, scope, JS_TRUE)) return NULL; spp = js_SearchScope(scope, id, JS_TRUE); sprop = overwriting = SPROP_FETCH(spp); } SCOPE_SET_MIDDLE_DELETE(scope); } } /* * If we fail later on trying to find or create a new sprop, we will * goto fail_overwrite and restore *spp from |overwriting|. Note that * we don't bother to keep scope->removedCount in sync, because we'll * fix up *spp and scope->entryCount shortly, no matter how control * flow returns from this function. */ if (scope->table) SPROP_STORE_PRESERVING_COLLISION(spp, NULL); scope->entryCount--; CHECK_ANCESTOR_LINE(scope, JS_TRUE); sprop = NULL; } if (!sprop) { /* * If properties were deleted from the middle of the list starting at * scope->lastProp, we may need to fork the property tree and squeeze * all deleted properties out of scope's ancestor line. Otherwise we * risk adding a node with the same id as a "middle" node, violating * the rule that properties along an ancestor line have distinct ids * (unless flagged SPROP_IS_DUPLICATE). */ if (SCOPE_HAD_MIDDLE_DELETE(scope)) { JS_ASSERT(scope->table); CHECK_ANCESTOR_LINE(scope, JS_TRUE); splen = scope->entryCount; if (splen == 0) { JS_ASSERT(scope->lastProp == NULL); } else { /* * Enumerate live entries in scope->table using a temporary * vector, by walking the (possibly sparse, due to deletions) * ancestor line from scope->lastProp. */ spvec = (JSScopeProperty **) JS_malloc(cx, SCOPE_TABLE_NBYTES(splen)); if (!spvec) goto fail_overwrite; i = splen; sprop = SCOPE_LAST_PROP(scope); JS_ASSERT(sprop); do { /* * NB: test SCOPE_GET_PROPERTY, not SCOPE_HAS_PROPERTY -- * the latter insists that sprop->id maps to sprop, while * the former simply tests whether sprop->id is bound in * scope. We must allow for duplicate formal parameters * along the ancestor line, and fork them as needed. */ if (!SCOPE_GET_PROPERTY(scope, sprop->id)) continue; JS_ASSERT(sprop != overwriting); if (i == 0) { /* * If our original splen estimate, scope->entryCount, * is less than the ancestor line height, there must * be duplicate formal parameters in this (function * object) scope. Count remaining ancestors in order * to realloc spvec. */ JSScopeProperty *tmp = sprop; do { if (SCOPE_GET_PROPERTY(scope, tmp->id)) i++; } while ((tmp = tmp->parent) != NULL); spp2 = (JSScopeProperty **) JS_realloc(cx, spvec, SCOPE_TABLE_NBYTES(splen+i)); if (!spp2) { JS_free(cx, spvec); goto fail_overwrite; } spvec = spp2; memmove(spvec + i, spvec, SCOPE_TABLE_NBYTES(splen)); splen += i; } spvec[--i] = sprop; } while ((sprop = sprop->parent) != NULL); JS_ASSERT(i == 0); /* * Now loop forward through spvec, forking the property tree * whenever we see a "parent gap" due to deletions from scope. * NB: sprop is null on first entry to the loop body. */ do { if (spvec[i]->parent == sprop) { sprop = spvec[i]; } else { sprop = GetPropertyTreeChild(cx, sprop, spvec[i]); if (!sprop) { JS_free(cx, spvec); goto fail_overwrite; } spp2 = js_SearchScope(scope, sprop->id, JS_FALSE); JS_ASSERT(SPROP_FETCH(spp2) == spvec[i]); SPROP_STORE_PRESERVING_COLLISION(spp2, sprop); } } while (++i < splen); JS_free(cx, spvec); /* * Now sprop points to the last property in scope, where the * ancestor line from sprop to the root is dense w.r.t. scope: * it contains no nodes not mapped by scope->table, apart from * any stinking ECMA-mandated duplicate formal parameters. */ scope->lastProp = sprop; CHECK_ANCESTOR_LINE(scope, JS_FALSE); JS_RUNTIME_METER(cx->runtime, middleDeleteFixups); } SCOPE_CLR_MIDDLE_DELETE(scope); } /* * Aliases share another property's slot, passed in the |slot| param. * Shared properties have no slot. Unshared properties that do not * alias another property's slot get one here, but may lose it due to * a JS_ClearScope call. */ if (!(flags & SPROP_IS_ALIAS)) { if (attrs & JSPROP_SHARED) { slot = SPROP_INVALID_SLOT; } else { /* * We may have set slot from a nearly-matching sprop, above. * If so, we're overwriting that nearly-matching sprop, so we * can reuse its slot -- we don't need to allocate a new one. * Callers should therefore pass SPROP_INVALID_SLOT for all * non-alias, unshared property adds. */ if (slot != SPROP_INVALID_SLOT) JS_ASSERT(overwriting); else if (!js_AllocSlot(cx, scope->object, &slot)) goto fail_overwrite; } } /* * Check for a watchpoint on a deleted property; if one exists, change * setter to js_watch_set. * XXXbe this could get expensive with lots of watchpoints... */ if (!JS_CLIST_IS_EMPTY(&cx->runtime->watchPointList) && js_FindWatchPoint(cx->runtime, scope, id)) { JS_PUSH_TEMP_ROOT_SPROP(cx, overwriting, &tvr); setter = js_WrapWatchedSetter(cx, id, attrs, setter); JS_POP_TEMP_ROOT(cx, &tvr); if (!setter) goto fail_overwrite; } /* Find or create a property tree node labeled by our arguments. */ child.id = id; child.getter = getter; child.setter = setter; child.slot = slot; child.attrs = attrs; child.flags = flags; child.shortid = shortid; sprop = GetPropertyTreeChild(cx, scope->lastProp, &child); if (!sprop) goto fail_overwrite; /* Store the tree node pointer in the table entry for id. */ if (scope->table) SPROP_STORE_PRESERVING_COLLISION(spp, sprop); scope->entryCount++; scope->lastProp = sprop; CHECK_ANCESTOR_LINE(scope, JS_FALSE); if (!overwriting) { JS_RUNTIME_METER(cx->runtime, liveScopeProps); JS_RUNTIME_METER(cx->runtime, totalScopeProps); } /* * If we reach the hashing threshold, try to allocate scope->table. * If we can't (a rare event, preceded by swapping to death on most * modern OSes), stick with linear search rather than whining about * this little set-back. Therefore we must test !scope->table and * scope->entryCount >= SCOPE_HASH_THRESHOLD, not merely whether the * entry count just reached the threshold. */ if (!scope->table && scope->entryCount >= SCOPE_HASH_THRESHOLD) (void) CreateScopeTable(cx, scope, JS_FALSE); } METER(adds); return sprop; fail_overwrite: if (overwriting) { /* * We may or may not have forked overwriting out of scope's ancestor * line, so we must check (the alternative is to set a flag above, but * that hurts the common, non-error case). If we did fork overwriting * out, we'll add it back at scope->lastProp. This means enumeration * order can change due to a failure to overwrite an id. * XXXbe very minor incompatibility */ for (sprop = SCOPE_LAST_PROP(scope); ; sprop = sprop->parent) { if (!sprop) { sprop = SCOPE_LAST_PROP(scope); if (overwriting->parent == sprop) { scope->lastProp = overwriting; } else { sprop = GetPropertyTreeChild(cx, sprop, overwriting); if (sprop) { JS_ASSERT(sprop != overwriting); scope->lastProp = sprop; } overwriting = sprop; } break; } if (sprop == overwriting) break; } if (overwriting) { if (scope->table) SPROP_STORE_PRESERVING_COLLISION(spp, overwriting); scope->entryCount++; } CHECK_ANCESTOR_LINE(scope, JS_TRUE); } METER(addFailures); return NULL; } JSScopeProperty * js_ChangeScopePropertyAttrs(JSContext *cx, JSScope *scope, JSScopeProperty *sprop, uintN attrs, uintN mask, JSPropertyOp getter, JSPropertyOp setter) { JSScopeProperty child, *newsprop, **spp; CHECK_ANCESTOR_LINE(scope, JS_TRUE); /* Allow only shared (slot-less) => unshared (slot-full) transition. */ attrs |= sprop->attrs & mask; JS_ASSERT(!((attrs ^ sprop->attrs) & JSPROP_SHARED) || !(attrs & JSPROP_SHARED)); if (getter == JS_PropertyStub) getter = NULL; if (setter == JS_PropertyStub) setter = NULL; if (sprop->attrs == attrs && sprop->getter == getter && sprop->setter == setter) { return sprop; } child.id = sprop->id; child.getter = getter; child.setter = setter; child.slot = sprop->slot; child.attrs = attrs; child.flags = sprop->flags; child.shortid = sprop->shortid; if (SCOPE_LAST_PROP(scope) == sprop) { /* * Optimize the case where the last property added to scope is changed * to have a different attrs, getter, or setter. In the last property * case, we need not fork the property tree. But since we do not call * js_AddScopeProperty, we may need to allocate a new slot directly. */ if ((sprop->attrs & JSPROP_SHARED) && !(attrs & JSPROP_SHARED)) { JS_ASSERT(child.slot == SPROP_INVALID_SLOT); if (!js_AllocSlot(cx, scope->object, &child.slot)) return NULL; } newsprop = GetPropertyTreeChild(cx, sprop->parent, &child); if (newsprop) { spp = js_SearchScope(scope, sprop->id, JS_FALSE); JS_ASSERT(SPROP_FETCH(spp) == sprop); if (scope->table) SPROP_STORE_PRESERVING_COLLISION(spp, newsprop); scope->lastProp = newsprop; CHECK_ANCESTOR_LINE(scope, JS_TRUE); } } else { /* * Let js_AddScopeProperty handle this |overwriting| case, including * the conservation of sprop->slot (if it's valid). We must not call * js_RemoveScopeProperty here, it will free a valid sprop->slot and * js_AddScopeProperty won't re-allocate it. */ newsprop = js_AddScopeProperty(cx, scope, child.id, child.getter, child.setter, child.slot, child.attrs, child.flags, child.shortid); } #ifdef DUMP_SCOPE_STATS if (!newsprop) METER(changeFailures); #endif return newsprop; } JSBool js_RemoveScopeProperty(JSContext *cx, JSScope *scope, jsid id) { JSScopeProperty **spp, *stored, *sprop; uint32 size; JS_ASSERT(JS_IS_SCOPE_LOCKED(cx, scope)); CHECK_ANCESTOR_LINE(scope, JS_TRUE); if (SCOPE_IS_SEALED(scope)) { ReportReadOnlyScope(cx, scope); return JS_FALSE; } METER(removes); spp = js_SearchScope(scope, id, JS_FALSE); stored = *spp; sprop = SPROP_CLEAR_COLLISION(stored); if (!sprop) { METER(uselessRemoves); return JS_TRUE; } /* Convert from a list to a hash so we can handle "middle deletes". */ if (!scope->table && sprop != scope->lastProp) { if (!CreateScopeTable(cx, scope, JS_TRUE)) return JS_FALSE; spp = js_SearchScope(scope, id, JS_FALSE); stored = *spp; sprop = SPROP_CLEAR_COLLISION(stored); } /* First, if sprop is unshared and not cleared, free its slot number. */ if (SPROP_HAS_VALID_SLOT(sprop, scope)) { js_FreeSlot(cx, scope->object, sprop->slot); JS_ATOMIC_INCREMENT(&cx->runtime->propertyRemovals); } /* Next, remove id by setting its entry to a removed or free sentinel. */ if (SPROP_HAD_COLLISION(stored)) { JS_ASSERT(scope->table); *spp = SPROP_REMOVED; scope->removedCount++; } else { METER(removeFrees); if (scope->table) *spp = NULL; } scope->entryCount--; JS_RUNTIME_UNMETER(cx->runtime, liveScopeProps); /* Update scope->lastProp directly, or set its deferred update flag. */ if (sprop == SCOPE_LAST_PROP(scope)) { do { SCOPE_REMOVE_LAST_PROP(scope); if (!SCOPE_HAD_MIDDLE_DELETE(scope)) break; sprop = SCOPE_LAST_PROP(scope); } while (sprop && !SCOPE_HAS_PROPERTY(scope, sprop)); } else if (!SCOPE_HAD_MIDDLE_DELETE(scope)) { SCOPE_SET_MIDDLE_DELETE(scope); } CHECK_ANCESTOR_LINE(scope, JS_TRUE); /* Last, consider shrinking scope's table if its load factor is <= .25. */ size = SCOPE_CAPACITY(scope); if (size > MIN_SCOPE_SIZE && scope->entryCount <= size >> 2) { METER(shrinks); (void) ChangeScope(cx, scope, -1); } return JS_TRUE; } void js_ClearScope(JSContext *cx, JSScope *scope) { CHECK_ANCESTOR_LINE(scope, JS_TRUE); #ifdef DEBUG JS_LOCK_RUNTIME_VOID(cx->runtime, cx->runtime->liveScopeProps -= scope->entryCount); #endif if (scope->table) free(scope->table); SCOPE_CLR_MIDDLE_DELETE(scope); InitMinimalScope(scope); JS_ATOMIC_INCREMENT(&cx->runtime->propertyRemovals); } void js_MarkId(JSContext *cx, jsid id) { if (JSID_IS_ATOM(id)) GC_MARK_ATOM(cx, JSID_TO_ATOM(id)); else if (JSID_IS_OBJECT(id)) GC_MARK(cx, JSID_TO_OBJECT(id), "id"); else JS_ASSERT(JSID_IS_INT(id)); } #if defined GC_MARK_DEBUG || defined DUMP_SCOPE_STATS # include "jsprf.h" #endif void js_MarkScopeProperty(JSContext *cx, JSScopeProperty *sprop) { sprop->flags |= SPROP_MARK; MARK_ID(cx, sprop->id); #if JS_HAS_GETTER_SETTER if (sprop->attrs & (JSPROP_GETTER | JSPROP_SETTER)) { #ifdef GC_MARK_DEBUG char buf[64]; char buf2[11]; const char *id; if (JSID_IS_ATOM(sprop->id)) { JSAtom *atom = JSID_TO_ATOM(sprop->id); id = (atom && ATOM_IS_STRING(atom)) ? JS_GetStringBytes(ATOM_TO_STRING(atom)) : "unknown"; } else if (JSID_IS_INT(sprop->id)) { JS_snprintf(buf2, sizeof buf2, "%d", JSID_TO_INT(sprop->id)); id = buf2; } else { id = ""; } #endif if (sprop->attrs & JSPROP_GETTER) { #ifdef GC_MARK_DEBUG JS_snprintf(buf, sizeof buf, "%s %s", id, js_getter_str); #endif GC_MARK(cx, JSVAL_TO_GCTHING((jsval) sprop->getter), buf); } if (sprop->attrs & JSPROP_SETTER) { #ifdef GC_MARK_DEBUG JS_snprintf(buf, sizeof buf, "%s %s", id, js_setter_str); #endif GC_MARK(cx, JSVAL_TO_GCTHING((jsval) sprop->setter), buf); } } #endif /* JS_HAS_GETTER_SETTER */ } #ifdef DUMP_SCOPE_STATS #include #include uint32 js_nkids_max; uint32 js_nkids_sum; double js_nkids_sqsum; uint32 js_nkids_hist[11]; static void MeterKidCount(uintN nkids) { if (nkids) { js_nkids_sum += nkids; js_nkids_sqsum += (double)nkids * nkids; if (nkids > js_nkids_max) js_nkids_max = nkids; } js_nkids_hist[JS_MIN(nkids, 10)]++; } static void MeterPropertyTree(JSScopeProperty *node) { uintN i, nkids; JSScopeProperty *kids, *kid; PropTreeKidsChunk *chunk; nkids = 0; kids = node->kids; if (kids) { if (KIDS_IS_CHUNKY(kids)) { for (chunk = KIDS_TO_CHUNK(kids); chunk; chunk = chunk->next) { for (i = 0; i < MAX_KIDS_PER_CHUNK; i++) { kid = chunk->kids[i]; if (!kid) break; MeterPropertyTree(kid); nkids++; } } } else { MeterPropertyTree(kids); nkids = 1; } } MeterKidCount(nkids); } JS_STATIC_DLL_CALLBACK(JSDHashOperator) js_MeterPropertyTree(JSDHashTable *table, JSDHashEntryHdr *hdr, uint32 number, void *arg) { JSPropertyTreeEntry *entry = (JSPropertyTreeEntry *)hdr; MeterPropertyTree(entry->child); return JS_DHASH_NEXT; } static void DumpSubtree(JSScopeProperty *sprop, int level, FILE *fp) { char buf[10]; JSScopeProperty *kids, *kid; PropTreeKidsChunk *chunk; uintN i; fprintf(fp, "%*sid %s g/s %p/%p slot %lu attrs %x flags %x shortid %d\n", level, "", JSID_IS_ATOM(sprop->id) ? JS_GetStringBytes(ATOM_TO_STRING(JSID_TO_ATOM(sprop->id))) : JSID_IS_OBJECT(sprop->id) ? js_ValueToPrintableString(cx, OBJECT_JSID_TO_JSVAL(sprop->id)) : (JS_snprintf(buf, sizeof buf, "%ld", JSVAL_TO_INT(sprop->id)), buf) (void *) sprop->getter, (void *) sprop->setter, (unsigned long) sprop->slot, sprop->attrs, sprop->flags, sprop->shortid); kids = sprop->kids; if (kids) { ++level; if (KIDS_IS_CHUNKY(kids)) { chunk = KIDS_TO_CHUNK(kids); do { for (i = 0; i < MAX_KIDS_PER_CHUNK; i++) { kid = chunk->kids[i]; if (!kid) break; JS_ASSERT(kid->parent == sprop); DumpSubtree(kid, level, fp); } } while ((chunk = chunk->next) != NULL); } else { kid = kids; DumpSubtree(kid, level, fp); } } } #endif /* DUMP_SCOPE_STATS */ void js_SweepScopeProperties(JSRuntime *rt) { JSArena **ap, *a; JSScopeProperty *limit, *sprop, *parent, *kids, *kid; uintN liveCount; PropTreeKidsChunk *chunk, *nextChunk, *freeChunk; uintN i; #ifdef DUMP_SCOPE_STATS uint32 livePropCapacity = 0, totalLiveCount = 0; static FILE *logfp; if (!logfp) logfp = fopen("/tmp/proptree.stats", "a"); MeterKidCount(rt->propertyTreeHash.entryCount); JS_DHashTableEnumerate(&rt->propertyTreeHash, js_MeterPropertyTree, NULL); { double mean = 0.0, var = 0.0, sigma = 0.0; double nodesum = rt->livePropTreeNodes; double kidsum = js_nkids_sum; if (nodesum > 0 && kidsum >= 0) { mean = kidsum / nodesum; var = nodesum * js_nkids_sqsum - kidsum * kidsum; if (var < 0.0 || nodesum <= 1) var = 0.0; else var /= nodesum * (nodesum - 1); /* Windows says sqrt(0.0) is "-1.#J" (?!) so we must test. */ sigma = (var != 0.0) ? sqrt(var) : 0.0; } fprintf(logfp, "props %u nodes %g beta %g meankids %g sigma %g max %u", rt->liveScopeProps, nodesum, nodesum / rt->liveScopeProps, mean, sigma, js_nkids_max); } fprintf(logfp, " histogram %u %u %u %u %u %u %u %u %u %u %u", js_nkids_hist[0], js_nkids_hist[1], js_nkids_hist[2], js_nkids_hist[3], js_nkids_hist[4], js_nkids_hist[5], js_nkids_hist[6], js_nkids_hist[7], js_nkids_hist[8], js_nkids_hist[9], js_nkids_hist[10]); js_nkids_sum = js_nkids_max = 0; js_nkids_sqsum = 0; memset(js_nkids_hist, 0, sizeof js_nkids_hist); #endif ap = &rt->propertyArenaPool.first.next; while ((a = *ap) != NULL) { limit = (JSScopeProperty *) a->avail; liveCount = 0; for (sprop = (JSScopeProperty *) a->base; sprop < limit; sprop++) { /* If the id is null, sprop is already on the freelist. */ if (sprop->id == JSVAL_NULL) continue; /* If the mark bit is set, sprop is alive, so we skip it. */ if (sprop->flags & SPROP_MARK) { sprop->flags &= ~SPROP_MARK; liveCount++; continue; } /* Ok, sprop is garbage to collect: unlink it from its parent. */ freeChunk = RemovePropertyTreeChild(rt, sprop); /* * Take care to reparent all sprop's kids to their grandparent. * InsertPropertyTreeChild can potentially fail for two reasons: * * 1. If parent is null, insertion into the root property hash * table may fail. We are forced to leave the kid out of the * table (as can already happen with duplicates) but ensure * that the kid's parent pointer is set to null. * * 2. If parent is non-null, allocation of a new KidsChunk can * fail. To prevent this from happening, we allow sprops's own * chunks to be reused by the grandparent, which removes the * need for InsertPropertyTreeChild to malloc a new KidsChunk. * * If sprop does not have chunky kids, then we rely on the * RemovePropertyTreeChild call above (which removed sprop from * its parent) either leaving one free entry, or else returning * the now-unused chunk to us so we can reuse it. * * We also require the grandparent to have either no kids or else * chunky kids. A single non-chunky kid would force a new chunk to * be malloced in some cases (if sprop had a single non-chunky * kid, or a multiple of MAX_KIDS_PER_CHUNK kids). Note that * RemovePropertyTreeChild never converts a single-entry chunky * kid back to a non-chunky kid, so we are assured of correct * behaviour. */ kids = sprop->kids; if (kids) { sprop->kids = NULL; parent = sprop->parent; /* Validate that grandparent has no kids or chunky kids. */ JS_ASSERT(!parent || !parent->kids || KIDS_IS_CHUNKY(parent->kids)); if (KIDS_IS_CHUNKY(kids)) { chunk = KIDS_TO_CHUNK(kids); do { nextChunk = chunk->next; chunk->next = NULL; for (i = 0; i < MAX_KIDS_PER_CHUNK; i++) { kid = chunk->kids[i]; if (!kid) break; JS_ASSERT(kid->parent == sprop); /* * Clear a space in the kids array for possible * re-use by InsertPropertyTreeChild. */ chunk->kids[i] = NULL; if (!InsertPropertyTreeChild(rt, parent, kid, chunk)) { /* * This can happen only if we failed to add an * entry to the root property hash table. */ JS_ASSERT(!parent); kid->parent = NULL; } } if (!chunk->kids[0]) { /* The chunk wasn't reused, so we must free it. */ DestroyPropTreeKidsChunk(rt, chunk); } } while ((chunk = nextChunk) != NULL); } else { kid = kids; if (!InsertPropertyTreeChild(rt, parent, kid, freeChunk)) { /* * This can happen only if we failed to add an entry * to the root property hash table. */ JS_ASSERT(!parent); kid->parent = NULL; } } } if (freeChunk && !freeChunk->kids[0]) { /* The chunk wasn't reused, so we must free it. */ DestroyPropTreeKidsChunk(rt, freeChunk); } /* Clear id so we know (above) that sprop is on the freelist. */ sprop->id = JSVAL_NULL; FREENODE_INSERT(rt->propertyFreeList, sprop); JS_RUNTIME_UNMETER(rt, livePropTreeNodes); } /* If a contains no live properties, return it to the malloc heap. */ if (liveCount == 0) { for (sprop = (JSScopeProperty *) a->base; sprop < limit; sprop++) FREENODE_REMOVE(sprop); JS_ARENA_DESTROY(&rt->propertyArenaPool, a, ap); } else { #ifdef DUMP_SCOPE_STATS livePropCapacity += limit - (JSScopeProperty *) a->base; totalLiveCount += liveCount; #endif ap = &a->next; } } #ifdef DUMP_SCOPE_STATS fprintf(logfp, " arenautil %g%%\n", (totalLiveCount * 100.0) / livePropCapacity); fflush(logfp); #endif #ifdef DUMP_PROPERTY_TREE { FILE *dumpfp = fopen("/tmp/proptree.dump", "w"); if (dumpfp) { JSPropertyTreeEntry *pte, *end; pte = (JSPropertyTreeEntry *) rt->propertyTreeHash.entryStore; end = pte + JS_DHASH_TABLE_SIZE(&rt->propertyTreeHash); while (pte < end) { if (pte->child) DumpSubtree(pte->child, 0, dumpfp); pte++; } fclose(dumpfp); } } #endif } JSBool js_InitPropertyTree(JSRuntime *rt) { if (!JS_DHashTableInit(&rt->propertyTreeHash, &PropertyTreeHashOps, NULL, sizeof(JSPropertyTreeEntry), JS_DHASH_MIN_SIZE)) { rt->propertyTreeHash.ops = NULL; return JS_FALSE; } JS_InitArenaPool(&rt->propertyArenaPool, "properties", 256 * sizeof(JSScopeProperty), sizeof(void *)); return JS_TRUE; } void js_FinishPropertyTree(JSRuntime *rt) { if (rt->propertyTreeHash.ops) { JS_DHashTableFinish(&rt->propertyTreeHash); rt->propertyTreeHash.ops = NULL; } JS_FinishArenaPool(&rt->propertyArenaPool); } pacparser-1.4.5/src/spidermonkey/js/src/jsscope.h000066400000000000000000000470171464010763600220430ustar00rootroot00000000000000/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- * vim: set ts=8 sw=4 et tw=78: * * ***** BEGIN LICENSE BLOCK ***** * Version: MPL 1.1/GPL 2.0/LGPL 2.1 * * The contents of this file are subject to the Mozilla Public License Version * 1.1 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * http://www.mozilla.org/MPL/ * * Software distributed under the License is distributed on an "AS IS" basis, * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License * for the specific language governing rights and limitations under the * License. * * The Original Code is Mozilla Communicator client code, released * March 31, 1998. * * The Initial Developer of the Original Code is * Netscape Communications Corporation. * Portions created by the Initial Developer are Copyright (C) 1998 * the Initial Developer. All Rights Reserved. * * Contributor(s): * * Alternatively, the contents of this file may be used under the terms of * either of the GNU General Public License Version 2 or later (the "GPL"), * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), * in which case the provisions of the GPL or the LGPL are applicable instead * of those above. If you wish to allow use of your version of this file only * under the terms of either the GPL or the LGPL, and not to allow others to * use your version of this file under the terms of the MPL, indicate your * decision by deleting the provisions above and replace them with the notice * and other provisions required by the GPL or the LGPL. If you do not delete * the provisions above, a recipient may use your version of this file under * the terms of any one of the MPL, the GPL or the LGPL. * * ***** END LICENSE BLOCK ***** */ #ifndef jsscope_h___ #define jsscope_h___ /* * JS symbol tables. */ #include "jstypes.h" #include "jsobj.h" #include "jsprvtd.h" #include "jspubtd.h" #ifdef JS_THREADSAFE # include "jslock.h" #endif /* * Given P independent, non-unique properties each of size S words mapped by * all scopes in a runtime, construct a property tree of N nodes each of size * S+L words (L for tree linkage). A nominal L value is 2 for leftmost-child * and right-sibling links. We hope that the N < P by enough that the space * overhead of L, and the overhead of scope entries pointing at property tree * nodes, is worth it. * * The tree construction goes as follows. If any empty scope in the runtime * has a property X added to it, find or create a node under the tree root * labeled X, and set scope->lastProp to point at that node. If any non-empty * scope whose most recently added property is labeled Y has another property * labeled Z added, find or create a node for Z under the node that was added * for Y, and set scope->lastProp to point at that node. * * A property is labeled by its members' values: id, getter, setter, slot, * attributes, tiny or short id, and a field telling for..in order. Note that * labels are not unique in the tree, but they are unique among a node's kids * (barring rare and benign multi-threaded race condition outcomes, see below) * and along any ancestor line from the tree root to a given leaf node (except * for the hard case of duplicate formal parameters to a function). * * Thus the root of the tree represents all empty scopes, and the first ply * of the tree represents all scopes containing one property, etc. Each node * in the tree can stand for any number of scopes having the same ordered set * of properties, where that node was the last added to the scope. (We need * not store the root of the tree as a node, and do not -- all we need are * links to its kids.) * * Sidebar on for..in loop order: ECMA requires no particular order, but this * implementation has promised and delivered property definition order, and * compatibility is king. We could use an order number per property, which * would require a sort in js_Enumerate, and an entry order generation number * per scope. An order number beats a list, which should be doubly-linked for * O(1) delete. An even better scheme is to use a parent link in the property * tree, so that the ancestor line can be iterated from scope->lastProp when * filling in a JSIdArray from back to front. This parent link also helps the * GC to sweep properties iteratively. * * What if a property Y is deleted from a scope? If Y is the last property in * the scope, we simply adjust the scope's lastProp member after we remove the * scope's hash-table entry pointing at that property node. The parent link * mentioned in the for..in sidebar above makes this adjustment O(1). But if * Y comes between X and Z in the scope, then we might have to "fork" the tree * at X, leaving X->Y->Z in case other scopes have those properties added in * that order; and to finish the fork, we'd add a node labeled Z with the path * X->Z, if it doesn't exist. This could lead to lots of extra nodes, and to * O(n^2) growth when deleting lots of properties. * * Rather, for O(1) growth all around, we should share the path X->Y->Z among * scopes having those three properties added in that order, and among scopes * having only X->Z where Y was deleted. All such scopes have a lastProp that * points to the Z child of Y. But a scope in which Y was deleted does not * have a table entry for Y, and when iterating that scope by traversing the * ancestor line from Z, we will have to test for a table entry for each node, * skipping nodes that lack entries. * * What if we add Y again? X->Y->Z->Y is wrong and we'll enumerate Y twice. * Therefore we must fork in such a case, if not earlier. Because delete is * "bursty", we should not fork eagerly. Delaying a fork till we are at risk * of adding Y after it was deleted already requires a flag in the JSScope, to * wit, SCOPE_MIDDLE_DELETE. * * What about thread safety? If the property tree operations done by requests * are find-node and insert-node, then the only hazard is duplicate insertion. * This is harmless except for minor bloat. When all requests have ended or * been suspended, the GC is free to sweep the tree after marking all nodes * reachable from scopes, performing remove-node operations as needed. Note * also that the stable storage of the property nodes during active requests * permits the property cache (see jsinterp.h) to dereference JSScopeProperty * weak references safely. * * Is the property tree worth it compared to property storage in each table's * entries? To decide, we must find the relation <> between the words used * with a property tree and the words required without a tree. * * Model all scopes as one super-scope of capacity T entries (T a power of 2). * Let alpha be the load factor of this double hash-table. With the property * tree, each entry in the table is a word-sized pointer to a node that can be * shared by many scopes. But all such pointers are overhead compared to the * situation without the property tree, where the table stores property nodes * directly, as entries each of size S words. With the property tree, we need * L=2 extra words per node for siblings and kids pointers. Without the tree, * (1-alpha)*S*T words are wasted on free or removed sentinel-entries required * by double hashing. * * Therefore, * * (property tree) <> (no property tree) * N*(S+L) + T <> S*T * N*(S+L) + T <> P*S + (1-alpha)*S*T * N*(S+L) + alpha*T + (1-alpha)*T <> P*S + (1-alpha)*S*T * * Note that P is alpha*T by definition, so * * N*(S+L) + P + (1-alpha)*T <> P*S + (1-alpha)*S*T * N*(S+L) <> P*S - P + (1-alpha)*S*T - (1-alpha)*T * N*(S+L) <> (P + (1-alpha)*T) * (S-1) * N*(S+L) <> (P + (1-alpha)*P/alpha) * (S-1) * N*(S+L) <> P * (1/alpha) * (S-1) * * Let N = P*beta for a compression ratio beta, beta <= 1: * * P*beta*(S+L) <> P * (1/alpha) * (S-1) * beta*(S+L) <> (S-1)/alpha * beta <> (S-1)/((S+L)*alpha) * * For S = 6 (32-bit architectures) and L = 2, the property tree wins iff * * beta < 5/(8*alpha) * * We ensure that alpha <= .75, so the property tree wins if beta < .83_. An * average beta from recent Mozilla browser startups was around .6. * * Can we reduce L? Observe that the property tree degenerates into a list of * lists if at most one property Y follows X in all scopes. In or near such a * case, we waste a word on the right-sibling link outside of the root ply of * the tree. Note also that the root ply tends to be large, so O(n^2) growth * searching it is likely, indicating the need for hashing (but with increased * thread safety costs). * * If only K out of N nodes in the property tree have more than one child, we * could eliminate the sibling link and overlay a children list or hash-table * pointer on the leftmost-child link (which would then be either null or an * only-child link; the overlay could be tagged in the low bit of the pointer, * or flagged elsewhere in the property tree node, although such a flag must * not be considered when comparing node labels during tree search). * * For such a system, L = 1 + (K * averageChildrenTableSize) / N instead of 2. * If K << N, L approaches 1 and the property tree wins if beta < .95. * * We observe that fan-out below the root ply of the property tree appears to * have extremely low degree (see the MeterPropertyTree code that histograms * child-counts in jsscope.c), so instead of a hash-table we use a linked list * of child node pointer arrays ("kid chunks"). The details are isolated in * jsscope.c; others must treat JSScopeProperty.kids as opaque. We leave it * strongly typed for debug-ability of the common (null or one-kid) cases. * * One final twist (can you stand it?): the mean number of entries per scope * in Mozilla is < 5, with a large standard deviation (~8). Instead of always * allocating scope->table, we leave it null while initializing all the other * scope members as if it were non-null and minimal-length. Until a property * is added that crosses the threshold of 6 or more entries for hashing, or * until a "middle delete" occurs, we use linear search from scope->lastProp * to find a given id, and save on the space overhead of a hash table. */ struct JSScope { JSObjectMap map; /* base class state */ JSObject *object; /* object that owns this scope */ uint8 flags; /* flags, see below */ int8 hashShift; /* multiplicative hash shift */ uint16 spare; /* reserved */ uint32 entryCount; /* number of entries in table */ uint32 removedCount; /* removed entry sentinels in table */ JSScopeProperty **table; /* table of ptrs to shared tree nodes */ JSScopeProperty *lastProp; /* pointer to last property added */ #ifdef JS_THREADSAFE JSContext *ownercx; /* creating context, NULL if shared */ JSThinLock lock; /* binary semaphore protecting scope */ union { /* union lockful and lock-free state: */ jsrefcount count; /* lock entry count for reentrancy */ JSScope *link; /* next link in rt->scopeSharingTodo */ } u; #ifdef DEBUG const char *file[4]; /* file where lock was (re-)taken */ unsigned int line[4]; /* line where lock was (re-)taken */ #endif #endif }; #define OBJ_SCOPE(obj) ((JSScope *)(obj)->map) /* By definition, hashShift = JS_DHASH_BITS - log2(capacity). */ #define SCOPE_CAPACITY(scope) JS_BIT(JS_DHASH_BITS-(scope)->hashShift) /* Scope flags and some macros to hide them from other files than jsscope.c. */ #define SCOPE_MIDDLE_DELETE 0x0001 #define SCOPE_SEALED 0x0002 #define SCOPE_HAD_MIDDLE_DELETE(scope) ((scope)->flags & SCOPE_MIDDLE_DELETE) #define SCOPE_SET_MIDDLE_DELETE(scope) ((scope)->flags |= SCOPE_MIDDLE_DELETE) #define SCOPE_CLR_MIDDLE_DELETE(scope) ((scope)->flags &= ~SCOPE_MIDDLE_DELETE) #define SCOPE_IS_SEALED(scope) ((scope)->flags & SCOPE_SEALED) #define SCOPE_SET_SEALED(scope) ((scope)->flags |= SCOPE_SEALED) #if 0 /* * Don't define this, it can't be done safely because JS_LOCK_OBJ will avoid * taking the lock if the object owns its scope and the scope is sealed. */ #define SCOPE_CLR_SEALED(scope) ((scope)->flags &= ~SCOPE_SEALED) #endif /* * A little information hiding for scope->lastProp, in case it ever becomes * a tagged pointer again. */ #define SCOPE_LAST_PROP(scope) ((scope)->lastProp) #define SCOPE_REMOVE_LAST_PROP(scope) ((scope)->lastProp = \ (scope)->lastProp->parent) struct JSScopeProperty { jsid id; /* int-tagged jsval/untagged JSAtom* */ JSPropertyOp getter; /* getter and setter hooks or objects */ JSPropertyOp setter; uint32 slot; /* index in obj->slots vector */ uint8 attrs; /* attributes, see jsapi.h JSPROP_* */ uint8 flags; /* flags, see below for defines */ int16 shortid; /* tinyid, or local arg/var index */ JSScopeProperty *parent; /* parent node, reverse for..in order */ JSScopeProperty *kids; /* null, single child, or a tagged ptr to many-kids data structure */ }; /* JSScopeProperty pointer tag bit indicating a collision. */ #define SPROP_COLLISION ((jsuword)1) #define SPROP_REMOVED ((JSScopeProperty *) SPROP_COLLISION) /* Macros to get and set sprop pointer values and collision flags. */ #define SPROP_IS_FREE(sprop) ((sprop) == NULL) #define SPROP_IS_REMOVED(sprop) ((sprop) == SPROP_REMOVED) #define SPROP_IS_LIVE(sprop) ((sprop) > SPROP_REMOVED) #define SPROP_FLAG_COLLISION(spp,sprop) (*(spp) = (JSScopeProperty *) \ ((jsuword)(sprop) | SPROP_COLLISION)) #define SPROP_HAD_COLLISION(sprop) ((jsuword)(sprop) & SPROP_COLLISION) #define SPROP_FETCH(spp) SPROP_CLEAR_COLLISION(*(spp)) #define SPROP_CLEAR_COLLISION(sprop) \ ((JSScopeProperty *) ((jsuword)(sprop) & ~SPROP_COLLISION)) #define SPROP_STORE_PRESERVING_COLLISION(spp, sprop) \ (*(spp) = (JSScopeProperty *) ((jsuword)(sprop) \ | SPROP_HAD_COLLISION(*(spp)))) /* Bits stored in sprop->flags. */ #define SPROP_MARK 0x01 #define SPROP_IS_DUPLICATE 0x02 #define SPROP_IS_ALIAS 0x04 #define SPROP_HAS_SHORTID 0x08 #define SPROP_IS_HIDDEN 0x10 /* a normally-hidden property, e.g., function arg or var */ /* * If SPROP_HAS_SHORTID is set in sprop->flags, we use sprop->shortid rather * than id when calling sprop's getter or setter. */ #define SPROP_USERID(sprop) \ (((sprop)->flags & SPROP_HAS_SHORTID) ? INT_TO_JSVAL((sprop)->shortid) \ : ID_TO_VALUE((sprop)->id)) #define SPROP_INVALID_SLOT 0xffffffff #define SLOT_IN_SCOPE(slot,scope) ((slot) < (scope)->map.freeslot) #define SPROP_HAS_VALID_SLOT(sprop,scope) SLOT_IN_SCOPE((sprop)->slot, scope) #define SPROP_HAS_STUB_GETTER(sprop) (!(sprop)->getter) #define SPROP_HAS_STUB_SETTER(sprop) (!(sprop)->setter) /* * NB: SPROP_GET must not be called if SPROP_HAS_STUB_GETTER(sprop). */ #define SPROP_GET(cx,sprop,obj,obj2,vp) \ (((sprop)->attrs & JSPROP_GETTER) \ ? js_InternalGetOrSet(cx, obj, (sprop)->id, \ OBJECT_TO_JSVAL((sprop)->getter), JSACC_READ, \ 0, 0, vp) \ : (sprop)->getter(cx, OBJ_THIS_OBJECT(cx,obj), SPROP_USERID(sprop), vp)) /* * NB: SPROP_SET must not be called if (SPROP_HAS_STUB_SETTER(sprop) && * !(sprop->attrs & JSPROP_GETTER)). */ #define SPROP_SET(cx,sprop,obj,obj2,vp) \ (((sprop)->attrs & JSPROP_SETTER) \ ? js_InternalGetOrSet(cx, obj, (sprop)->id, \ OBJECT_TO_JSVAL((sprop)->setter), JSACC_WRITE, \ 1, vp, vp) \ : ((sprop)->attrs & JSPROP_GETTER) \ ? (JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, \ JSMSG_GETTER_ONLY, NULL), JS_FALSE) \ : (sprop)->setter(cx, OBJ_THIS_OBJECT(cx,obj), SPROP_USERID(sprop), vp)) /* Macro for common expression to test for shared permanent attributes. */ #define SPROP_IS_SHARED_PERMANENT(sprop) \ ((~(sprop)->attrs & (JSPROP_SHARED | JSPROP_PERMANENT)) == 0) extern JSScope * js_GetMutableScope(JSContext *cx, JSObject *obj); extern JSScope * js_NewScope(JSContext *cx, jsrefcount nrefs, JSObjectOps *ops, JSClass *clasp, JSObject *obj); extern void js_DestroyScope(JSContext *cx, JSScope *scope); #define ID_TO_VALUE(id) (JSID_IS_ATOM(id) ? ATOM_JSID_TO_JSVAL(id) : \ JSID_IS_OBJECT(id) ? OBJECT_JSID_TO_JSVAL(id) : \ (jsval)(id)) #define HASH_ID(id) (JSID_IS_ATOM(id) ? JSID_TO_ATOM(id)->number : \ JSID_IS_OBJECT(id) ? (jsatomid) JSID_CLRTAG(id) : \ (jsatomid) JSID_TO_INT(id)) extern JS_FRIEND_API(JSScopeProperty **) js_SearchScope(JSScope *scope, jsid id, JSBool adding); #define SCOPE_GET_PROPERTY(scope, id) \ SPROP_FETCH(js_SearchScope(scope, id, JS_FALSE)) #define SCOPE_HAS_PROPERTY(scope, sprop) \ (SCOPE_GET_PROPERTY(scope, (sprop)->id) == (sprop)) extern JSScopeProperty * js_AddScopeProperty(JSContext *cx, JSScope *scope, jsid id, JSPropertyOp getter, JSPropertyOp setter, uint32 slot, uintN attrs, uintN flags, intN shortid); extern JSScopeProperty * js_ChangeScopePropertyAttrs(JSContext *cx, JSScope *scope, JSScopeProperty *sprop, uintN attrs, uintN mask, JSPropertyOp getter, JSPropertyOp setter); extern JSBool js_RemoveScopeProperty(JSContext *cx, JSScope *scope, jsid id); extern void js_ClearScope(JSContext *cx, JSScope *scope); /* * These macros used to inline short code sequences, but they grew over time. * We retain them for internal backward compatibility, and in case one or both * ever shrink to inline-able size. */ #define MARK_ID(cx,id) js_MarkId(cx, id) #define MARK_SCOPE_PROPERTY(cx,sprop) js_MarkScopeProperty(cx, sprop) extern void js_MarkId(JSContext *cx, jsid id); extern void js_MarkScopeProperty(JSContext *cx, JSScopeProperty *sprop); extern void js_SweepScopeProperties(JSRuntime *rt); extern JSBool js_InitPropertyTree(JSRuntime *rt); extern void js_FinishPropertyTree(JSRuntime *rt); #endif /* jsscope_h___ */ pacparser-1.4.5/src/spidermonkey/js/src/jsscript.c000066400000000000000000001442311464010763600222250ustar00rootroot00000000000000/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- * vim: set ts=8 sw=4 et tw=78: * * ***** BEGIN LICENSE BLOCK ***** * Version: MPL 1.1/GPL 2.0/LGPL 2.1 * * The contents of this file are subject to the Mozilla Public License Version * 1.1 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * http://www.mozilla.org/MPL/ * * Software distributed under the License is distributed on an "AS IS" basis, * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License * for the specific language governing rights and limitations under the * License. * * The Original Code is Mozilla Communicator client code, released * March 31, 1998. * * The Initial Developer of the Original Code is * Netscape Communications Corporation. * Portions created by the Initial Developer are Copyright (C) 1998 * the Initial Developer. All Rights Reserved. * * Contributor(s): * * Alternatively, the contents of this file may be used under the terms of * either of the GNU General Public License Version 2 or later (the "GPL"), * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), * in which case the provisions of the GPL or the LGPL are applicable instead * of those above. If you wish to allow use of your version of this file only * under the terms of either the GPL or the LGPL, and not to allow others to * use your version of this file under the terms of the MPL, indicate your * decision by deleting the provisions above and replace them with the notice * and other provisions required by the GPL or the LGPL. If you do not delete * the provisions above, a recipient may use your version of this file under * the terms of any one of the MPL, the GPL or the LGPL. * * ***** END LICENSE BLOCK ***** */ /* * JS script operations. */ #include "jsstddef.h" #include #include "jstypes.h" #include "jsutil.h" /* Added by JSIFY */ #include "jsprf.h" #include "jsapi.h" #include "jsatom.h" #include "jscntxt.h" #include "jsconfig.h" #include "jsdbgapi.h" #include "jsemit.h" #include "jsfun.h" #include "jsinterp.h" #include "jslock.h" #include "jsnum.h" #include "jsopcode.h" #include "jsscript.h" #if JS_HAS_XDR #include "jsxdrapi.h" #endif #if JS_HAS_SCRIPT_OBJECT static const char js_script_exec[] = "Script.prototype.exec"; static const char js_script_compile[] = "Script.prototype.compile"; /* * This routine requires that obj has been locked previously. */ static jsint GetScriptExecDepth(JSContext *cx, JSObject *obj) { jsval v; JS_ASSERT(JS_IS_OBJ_LOCKED(cx, obj)); v = LOCKED_OBJ_GET_SLOT(obj, JSSLOT_START(&js_ScriptClass)); return JSVAL_TO_INT(v); } static void AdjustScriptExecDepth(JSContext *cx, JSObject *obj, jsint delta) { jsint execDepth; JS_LOCK_OBJ(cx, obj); execDepth = GetScriptExecDepth(cx, obj); LOCKED_OBJ_SET_SLOT(obj, JSSLOT_START(&js_ScriptClass), INT_TO_JSVAL(execDepth + delta)); JS_UNLOCK_OBJ(cx, obj); } #if JS_HAS_TOSOURCE static JSBool script_toSource(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) { uint32 indent; JSScript *script; size_t i, j, k, n; char buf[16]; jschar *s, *t; JSString *str; if (!JS_InstanceOf(cx, obj, &js_ScriptClass, argv)) return JS_FALSE; indent = 0; if (argc && !js_ValueToECMAUint32(cx, argv[0], &indent)) return JS_FALSE; script = (JSScript *) JS_GetPrivate(cx, obj); /* Let n count the source string length, j the "front porch" length. */ j = JS_snprintf(buf, sizeof buf, "(new %s(", js_ScriptClass.name); n = j + 2; if (!script) { /* Let k count the constructor argument string length. */ k = 0; s = NULL; /* quell GCC overwarning */ } else { str = JS_DecompileScript(cx, script, "Script.prototype.toSource", (uintN)indent); if (!str) return JS_FALSE; str = js_QuoteString(cx, str, '\''); if (!str) return JS_FALSE; s = JSSTRING_CHARS(str); k = JSSTRING_LENGTH(str); n += k; } /* Allocate the source string and copy into it. */ t = (jschar *) JS_malloc(cx, (n + 1) * sizeof(jschar)); if (!t) return JS_FALSE; for (i = 0; i < j; i++) t[i] = buf[i]; for (j = 0; j < k; i++, j++) t[i] = s[j]; t[i++] = ')'; t[i++] = ')'; t[i] = 0; /* Create and return a JS string for t. */ str = JS_NewUCString(cx, t, n); if (!str) { JS_free(cx, t); return JS_FALSE; } *rval = STRING_TO_JSVAL(str); return JS_TRUE; } #endif /* JS_HAS_TOSOURCE */ static JSBool script_toString(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) { uint32 indent; JSScript *script; JSString *str; indent = 0; if (argc && !js_ValueToECMAUint32(cx, argv[0], &indent)) return JS_FALSE; if (!JS_InstanceOf(cx, obj, &js_ScriptClass, argv)) return JS_FALSE; script = (JSScript *) JS_GetPrivate(cx, obj); if (!script) { *rval = STRING_TO_JSVAL(cx->runtime->emptyString); return JS_TRUE; } str = JS_DecompileScript(cx, script, "Script.prototype.toString", (uintN)indent); if (!str) return JS_FALSE; *rval = STRING_TO_JSVAL(str); return JS_TRUE; } static JSBool script_compile(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) { JSString *str; JSObject *scopeobj; jsval v; JSScript *script, *oldscript; JSStackFrame *fp, *caller; const char *file; uintN line; JSPrincipals *principals; jsint execDepth; /* Make sure obj is a Script object. */ if (!JS_InstanceOf(cx, obj, &js_ScriptClass, argv)) return JS_FALSE; /* If no args, leave private undefined and return early. */ if (argc == 0) goto out; /* Otherwise, the first arg is the script source to compile. */ str = js_ValueToString(cx, argv[0]); if (!str) return JS_FALSE; argv[0] = STRING_TO_JSVAL(str); scopeobj = NULL; if (argc >= 2) { if (!js_ValueToObject(cx, argv[1], &scopeobj)) return JS_FALSE; argv[1] = OBJECT_TO_JSVAL(scopeobj); } /* Compile using the caller's scope chain, which js_Invoke passes to fp. */ fp = cx->fp; caller = JS_GetScriptedCaller(cx, fp); JS_ASSERT(!caller || fp->scopeChain == caller->scopeChain); if (caller) { if (!scopeobj) { scopeobj = js_GetScopeChain(cx, caller); if (!scopeobj) return JS_FALSE; fp->scopeChain = scopeobj; /* for the compiler's benefit */ } principals = JS_EvalFramePrincipals(cx, fp, caller); if (principals == caller->script->principals) { file = caller->script->filename; line = js_PCToLineNumber(cx, caller->script, caller->pc); } else { file = principals->codebase; line = 0; } } else { file = NULL; line = 0; principals = NULL; } /* Ensure we compile this script with the right (inner) principals. */ scopeobj = js_CheckScopeChainValidity(cx, scopeobj, js_script_compile); if (!scopeobj) return JS_FALSE; /* * Compile the new script using the caller's scope chain, a la eval(). * Unlike jsobj.c:obj_eval, however, we do not set JSFRAME_EVAL in fp's * flags, because compilation is here separated from execution, and the * run-time scope chain may not match the compile-time. JSFRAME_EVAL is * tested in jsemit.c and jsscan.c to optimize based on identity of run- * and compile-time scope. */ fp->flags |= JSFRAME_SCRIPT_OBJECT; script = JS_CompileUCScriptForPrincipals(cx, scopeobj, principals, JSSTRING_CHARS(str), JSSTRING_LENGTH(str), file, line); if (!script) return JS_FALSE; JS_LOCK_OBJ(cx, obj); execDepth = GetScriptExecDepth(cx, obj); /* * execDepth must be 0 to allow compilation here, otherwise the JSScript * struct can be released while running. */ if (execDepth > 0) { JS_UNLOCK_OBJ(cx, obj); JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_COMPILE_EXECED_SCRIPT); return JS_FALSE; } /* Swap script for obj's old script, if any. */ v = LOCKED_OBJ_GET_SLOT(obj, JSSLOT_PRIVATE); oldscript = !JSVAL_IS_VOID(v) ? (JSScript *) JSVAL_TO_PRIVATE(v) : NULL; LOCKED_OBJ_SET_SLOT(obj, JSSLOT_PRIVATE, PRIVATE_TO_JSVAL(script)); JS_UNLOCK_OBJ(cx, obj); if (oldscript) js_DestroyScript(cx, oldscript); script->object = obj; js_CallNewScriptHook(cx, script, NULL); out: /* Return the object. */ *rval = OBJECT_TO_JSVAL(obj); return JS_TRUE; } static JSBool script_exec(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) { JSObject *scopeobj, *parent; JSStackFrame *fp, *caller; JSScript *script; JSBool ok; if (!JS_InstanceOf(cx, obj, &js_ScriptClass, argv)) return JS_FALSE; scopeobj = NULL; if (argc) { if (!js_ValueToObject(cx, argv[0], &scopeobj)) return JS_FALSE; argv[0] = OBJECT_TO_JSVAL(scopeobj); } /* * Emulate eval() by using caller's this, var object, sharp array, etc., * all propagated by js_Execute via a non-null fourth (down) argument to * js_Execute. If there is no scripted caller, js_Execute uses its second * (chain) argument to set the exec frame's varobj, thisp, and scopeChain. * * Unlike eval, which the compiler detects, Script.prototype.exec may be * called from a lightweight function, or even from native code (in which * case fp->varobj and fp->scopeChain are null). If exec is called from * a lightweight function, we will need to get a Call object representing * its frame, to act as the var object and scope chain head. */ fp = cx->fp; caller = JS_GetScriptedCaller(cx, fp); if (caller && !caller->varobj) { /* Called from a lightweight function. */ JS_ASSERT(caller->fun && !JSFUN_HEAVYWEIGHT_TEST(caller->fun->flags)); /* Scope chain links from Call object to callee's parent. */ parent = OBJ_GET_PARENT(cx, JSVAL_TO_OBJECT(caller->argv[-2])); if (!js_GetCallObject(cx, caller, parent)) return JS_FALSE; } if (!scopeobj) { /* No scope object passed in: try to use the caller's scope chain. */ if (caller) { /* * Load caller->scopeChain after the conditional js_GetCallObject * call above, which resets scopeChain as well as varobj. */ scopeobj = js_GetScopeChain(cx, caller); if (!scopeobj) return JS_FALSE; } else { /* * Called from native code, so we don't know what scope object to * use. We could use parent (see above), but Script.prototype.exec * might be a shared/sealed "superglobal" method. A more general * approach would use cx->globalObject, which will be the same as * exec.__parent__ in the non-superglobal case. In the superglobal * case it's the right object: the global, not the superglobal. */ scopeobj = cx->globalObject; } } scopeobj = js_CheckScopeChainValidity(cx, scopeobj, js_script_exec); if (!scopeobj) return JS_FALSE; /* Keep track of nesting depth for the script. */ AdjustScriptExecDepth(cx, obj, 1); /* Must get to out label after this */ script = (JSScript *) JS_GetPrivate(cx, obj); if (!script) { ok = JS_FALSE; goto out; } /* Belt-and-braces: check that this script object has access to scopeobj. */ ok = js_CheckPrincipalsAccess(cx, scopeobj, script->principals, CLASS_ATOM(cx, Script)); if (!ok) goto out; ok = js_Execute(cx, scopeobj, script, caller, JSFRAME_EVAL, rval); out: AdjustScriptExecDepth(cx, obj, -1); return ok; } #if JS_HAS_XDR static JSBool XDRAtomMap(JSXDRState *xdr, JSAtomMap *map) { JSContext *cx; uint32 natoms, i, index; JSAtom **atoms; cx = xdr->cx; if (xdr->mode == JSXDR_ENCODE) natoms = (uint32)map->length; if (!JS_XDRUint32(xdr, &natoms)) return JS_FALSE; if (xdr->mode == JSXDR_ENCODE) { atoms = map->vector; } else { if (natoms == 0) { atoms = NULL; } else { atoms = (JSAtom **) JS_malloc(cx, (size_t)natoms * sizeof *atoms); if (!atoms) return JS_FALSE; #ifdef DEBUG memset(atoms, 0, (size_t)natoms * sizeof *atoms); #endif } map->vector = atoms; map->length = natoms; } for (i = 0; i != natoms; ++i) { if (xdr->mode == JSXDR_ENCODE) index = i; if (!JS_XDRUint32(xdr, &index)) goto bad; /* * Assert that, when decoding, the read index is valid and points to * an unoccupied element of atoms array. */ JS_ASSERT(index < natoms); JS_ASSERT(xdr->mode == JSXDR_ENCODE || !atoms[index]); if (!js_XDRAtom(xdr, &atoms[index])) goto bad; } return JS_TRUE; bad: if (xdr->mode == JSXDR_DECODE) { JS_free(cx, atoms); map->vector = NULL; map->length = 0; } return JS_FALSE; } JSBool js_XDRScript(JSXDRState *xdr, JSScript **scriptp, JSBool *hasMagic) { JSContext *cx; JSScript *script, *newscript, *oldscript; uint32 length, lineno, depth, magic, nsrcnotes, ntrynotes; uint32 prologLength, version; JSBool filenameWasSaved; jssrcnote *notes, *sn; cx = xdr->cx; script = *scriptp; nsrcnotes = ntrynotes = 0; filenameWasSaved = JS_FALSE; notes = NULL; /* * Encode prologLength and version after script->length (_2 or greater), * but decode both new (>= _2) and old, prolog&version-free (_1) scripts. * Version _3 supports principals serialization. Version _4 reorders the * nsrcnotes and ntrynotes fields to come before everything except magic, * length, prologLength, and version, so that srcnote and trynote storage * can be allocated as part of the JSScript (along with bytecode storage). * * So far, the magic number has not changed for every jsopcode.tbl change. * We stipulate forward compatibility by requiring old bytecodes never to * change or go away (modulo a few exceptions before the XDR interfaces * evolved, and a few exceptions during active trunk development). With * the addition of JSOP_STOP to support JS_THREADED_INTERP, we make a new * magic number (_5) so that we know to append JSOP_STOP to old scripts * when deserializing. */ if (xdr->mode == JSXDR_ENCODE) magic = JSXDR_MAGIC_SCRIPT_CURRENT; if (!JS_XDRUint32(xdr, &magic)) return JS_FALSE; JS_ASSERT((uint32)JSXDR_MAGIC_SCRIPT_5 - (uint32)JSXDR_MAGIC_SCRIPT_1 == 4); if (magic - (uint32)JSXDR_MAGIC_SCRIPT_1 > 4) { if (!hasMagic) { JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_BAD_SCRIPT_MAGIC); return JS_FALSE; } *hasMagic = JS_FALSE; return JS_TRUE; } if (hasMagic) *hasMagic = JS_TRUE; if (xdr->mode == JSXDR_ENCODE) { length = script->length; prologLength = PTRDIFF(script->main, script->code, jsbytecode); JS_ASSERT((int16)script->version != JSVERSION_UNKNOWN); version = (uint32)script->version | (script->numGlobalVars << 16); lineno = (uint32)script->lineno; depth = (uint32)script->depth; /* Count the srcnotes, keeping notes pointing at the first one. */ notes = SCRIPT_NOTES(script); for (sn = notes; !SN_IS_TERMINATOR(sn); sn = SN_NEXT(sn)) continue; nsrcnotes = PTRDIFF(sn, notes, jssrcnote); nsrcnotes++; /* room for the terminator */ /* Count the trynotes. */ if (script->trynotes) { while (script->trynotes[ntrynotes].catchStart) ntrynotes++; ntrynotes++; /* room for the end marker */ } } if (!JS_XDRUint32(xdr, &length)) return JS_FALSE; if (magic >= JSXDR_MAGIC_SCRIPT_2) { if (!JS_XDRUint32(xdr, &prologLength)) return JS_FALSE; if (!JS_XDRUint32(xdr, &version)) return JS_FALSE; /* To fuse allocations, we need srcnote and trynote counts early. */ if (magic >= JSXDR_MAGIC_SCRIPT_4) { if (!JS_XDRUint32(xdr, &nsrcnotes)) return JS_FALSE; if (!JS_XDRUint32(xdr, &ntrynotes)) return JS_FALSE; } } if (xdr->mode == JSXDR_DECODE) { size_t alloclength = length; if (magic < JSXDR_MAGIC_SCRIPT_5) ++alloclength; /* add a byte for JSOP_STOP */ script = js_NewScript(cx, alloclength, nsrcnotes, ntrynotes); if (!script) return JS_FALSE; if (magic >= JSXDR_MAGIC_SCRIPT_2) { script->main += prologLength; script->version = (JSVersion) (version & 0xffff); script->numGlobalVars = (uint16) (version >> 16); /* If we know nsrcnotes, we allocated space for notes in script. */ if (magic >= JSXDR_MAGIC_SCRIPT_4) notes = SCRIPT_NOTES(script); } *scriptp = script; } /* * Control hereafter must goto error on failure, in order for the DECODE * case to destroy script and conditionally free notes, which if non-null * in the (DECODE and magic < _4) case must point at a temporary vector * allocated just below. */ oldscript = xdr->script; xdr->script = script; if (!JS_XDRBytes(xdr, (char *)script->code, length * sizeof(jsbytecode)) || !XDRAtomMap(xdr, &script->atomMap)) { goto error; } if (magic < JSXDR_MAGIC_SCRIPT_5) { if (xdr->mode == JSXDR_DECODE) { /* * Append JSOP_STOP to old scripts, to relieve the interpreter * from having to bounds-check pc. Also take care to increment * length, as it is used below and must count all bytecode. */ script->code[length++] = JSOP_STOP; } if (magic < JSXDR_MAGIC_SCRIPT_4) { if (!JS_XDRUint32(xdr, &nsrcnotes)) goto error; if (xdr->mode == JSXDR_DECODE) { notes = (jssrcnote *) JS_malloc(cx, nsrcnotes * sizeof(jssrcnote)); if (!notes) goto error; } } } if (!JS_XDRBytes(xdr, (char *)notes, nsrcnotes * sizeof(jssrcnote)) || !JS_XDRCStringOrNull(xdr, (char **)&script->filename) || !JS_XDRUint32(xdr, &lineno) || !JS_XDRUint32(xdr, &depth) || (magic < JSXDR_MAGIC_SCRIPT_4 && !JS_XDRUint32(xdr, &ntrynotes))) { goto error; } /* Script principals transcoding support comes with versions >= _3. */ if (magic >= JSXDR_MAGIC_SCRIPT_3) { JSPrincipals *principals; uint32 encodeable; if (xdr->mode == JSXDR_ENCODE) { principals = script->principals; encodeable = (cx->runtime->principalsTranscoder != NULL); if (!JS_XDRUint32(xdr, &encodeable)) goto error; if (encodeable && !cx->runtime->principalsTranscoder(xdr, &principals)) { goto error; } } else { if (!JS_XDRUint32(xdr, &encodeable)) goto error; if (encodeable) { if (!cx->runtime->principalsTranscoder) { JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_CANT_DECODE_PRINCIPALS); goto error; } if (!cx->runtime->principalsTranscoder(xdr, &principals)) goto error; script->principals = principals; } } } if (xdr->mode == JSXDR_DECODE) { const char *filename = script->filename; if (filename) { filename = js_SaveScriptFilename(cx, filename); if (!filename) goto error; JS_free(cx, (void *) script->filename); script->filename = filename; filenameWasSaved = JS_TRUE; } script->lineno = (uintN)lineno; script->depth = (uintN)depth; if (magic < JSXDR_MAGIC_SCRIPT_4) { /* * Argh, we have to reallocate script, copy notes into the extra * space after the bytecodes, and free the temporary notes vector. * First, add enough slop to nsrcnotes so we can align the address * after the srcnotes of the first trynote. */ uint32 osrcnotes = nsrcnotes; if (ntrynotes) nsrcnotes += JSTRYNOTE_ALIGNMASK; newscript = (JSScript *) JS_realloc(cx, script, sizeof(JSScript) + length * sizeof(jsbytecode) + nsrcnotes * sizeof(jssrcnote) + ntrynotes * sizeof(JSTryNote)); if (!newscript) goto error; *scriptp = script = newscript; script->code = (jsbytecode *)(script + 1); script->main = script->code + prologLength; memcpy(script->code + length, notes, osrcnotes * sizeof(jssrcnote)); JS_free(cx, (void *) notes); notes = NULL; if (ntrynotes) { script->trynotes = (JSTryNote *) ((jsword)(SCRIPT_NOTES(script) + nsrcnotes) & ~(jsword)JSTRYNOTE_ALIGNMASK); memset(script->trynotes, 0, ntrynotes * sizeof(JSTryNote)); } } } while (ntrynotes) { JSTryNote *tn = &script->trynotes[--ntrynotes]; uint32 start = (uint32) tn->start, catchLength = (uint32) tn->length, catchStart = (uint32) tn->catchStart; if (!JS_XDRUint32(xdr, &start) || !JS_XDRUint32(xdr, &catchLength) || !JS_XDRUint32(xdr, &catchStart)) { goto error; } tn->start = (ptrdiff_t) start; tn->length = (ptrdiff_t) catchLength; tn->catchStart = (ptrdiff_t) catchStart; } xdr->script = oldscript; return JS_TRUE; error: if (xdr->mode == JSXDR_DECODE) { if (script->filename && !filenameWasSaved) { JS_free(cx, (void *) script->filename); script->filename = NULL; } if (notes && magic < JSXDR_MAGIC_SCRIPT_4) JS_free(cx, (void *) notes); js_DestroyScript(cx, script); *scriptp = NULL; } return JS_FALSE; } #if JS_HAS_XDR_FREEZE_THAW /* * These cannot be exposed to web content, and chrome does not need them, so * we take them out of the Mozilla client altogether. Fortunately, there is * no way to serialize a native function (see fun_xdrObject in jsfun.c). */ static JSBool script_freeze(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) { JSXDRState *xdr; JSScript *script; JSBool ok, hasMagic; uint32 len; void *buf; JSString *str; if (!JS_InstanceOf(cx, obj, &js_ScriptClass, argv)) return JS_FALSE; script = (JSScript *) JS_GetPrivate(cx, obj); if (!script) return JS_TRUE; /* create new XDR */ xdr = JS_XDRNewMem(cx, JSXDR_ENCODE); if (!xdr) return JS_FALSE; /* write */ ok = js_XDRScript(xdr, &script, &hasMagic); if (!ok) goto out; if (!hasMagic) { *rval = JSVAL_VOID; goto out; } buf = JS_XDRMemGetData(xdr, &len); if (!buf) { ok = JS_FALSE; goto out; } JS_ASSERT((jsword)buf % sizeof(jschar) == 0); len /= sizeof(jschar); str = JS_NewUCStringCopyN(cx, (jschar *)buf, len); if (!str) { ok = JS_FALSE; goto out; } #if IS_BIG_ENDIAN { jschar *chars; uint32 i; /* Swap bytes in Unichars to keep frozen strings machine-independent. */ chars = JS_GetStringChars(str); for (i = 0; i < len; i++) chars[i] = JSXDR_SWAB16(chars[i]); } #endif *rval = STRING_TO_JSVAL(str); out: JS_XDRDestroy(xdr); return ok; } static JSBool script_thaw(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) { JSXDRState *xdr; JSString *str; void *buf; uint32 len; jsval v; JSScript *script, *oldscript; JSBool ok, hasMagic; if (!JS_InstanceOf(cx, obj, &js_ScriptClass, argv)) return JS_FALSE; if (argc == 0) return JS_TRUE; str = js_ValueToString(cx, argv[0]); if (!str) return JS_FALSE; argv[0] = STRING_TO_JSVAL(str); /* create new XDR */ xdr = JS_XDRNewMem(cx, JSXDR_DECODE); if (!xdr) return JS_FALSE; buf = JS_GetStringChars(str); len = JS_GetStringLength(str); #if IS_BIG_ENDIAN { jschar *from, *to; uint32 i; /* Swap bytes in Unichars to keep frozen strings machine-independent. */ from = (jschar *)buf; to = (jschar *) JS_malloc(cx, len * sizeof(jschar)); if (!to) { JS_XDRDestroy(xdr); return JS_FALSE; } for (i = 0; i < len; i++) to[i] = JSXDR_SWAB16(from[i]); buf = (char *)to; } #endif len *= sizeof(jschar); JS_XDRMemSetData(xdr, buf, len); /* XXXbe should magic mismatch be error, or false return value? */ ok = js_XDRScript(xdr, &script, &hasMagic); if (!ok) goto out; if (!hasMagic) { *rval = JSVAL_FALSE; goto out; } JS_LOCK_OBJ(cx, obj); execDepth = GetScriptExecDepth(cx, obj); /* * execDepth must be 0 to allow compilation here, otherwise the JSScript * struct can be released while running. */ if (execDepth > 0) { JS_UNLOCK_OBJ(cx, obj); JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_COMPILE_EXECED_SCRIPT); goto out; } /* Swap script for obj's old script, if any. */ v = LOCKED_OBJ_GET_SLOT(cx, obj, JSSLOT_PRIVATE); oldscript = !JSVAL_IS_VOID(v) ? (JSScript *) JSVAL_TO_PRIVATE(v) : NULL; LOCKED_OBJ_SET_SLOT(cx, obj, JSSLOT_PRIVATE, PRIVATE_TO_JSVAL(script)); JS_UNLOCK_OBJ(cx, obj); if (oldscript) js_DestroyScript(cx, oldscript); script->object = obj; js_CallNewScriptHook(cx, script, NULL); out: /* * We reset the buffer to be NULL so that it doesn't free the chars * memory owned by str (argv[0]). */ JS_XDRMemSetData(xdr, NULL, 0); JS_XDRDestroy(xdr); #if IS_BIG_ENDIAN JS_free(cx, buf); #endif *rval = JSVAL_TRUE; return ok; } static const char js_thaw_str[] = "thaw"; #endif /* JS_HAS_XDR_FREEZE_THAW */ #endif /* JS_HAS_XDR */ static JSFunctionSpec script_methods[] = { #if JS_HAS_TOSOURCE {js_toSource_str, script_toSource, 0,0,0}, #endif {js_toString_str, script_toString, 0,0,0}, {"compile", script_compile, 2,0,0}, {"exec", script_exec, 1,0,0}, #if JS_HAS_XDR_FREEZE_THAW {"freeze", script_freeze, 0,0,0}, {js_thaw_str, script_thaw, 1,0,0}, #endif /* JS_HAS_XDR_FREEZE_THAW */ {0,0,0,0,0} }; #endif /* JS_HAS_SCRIPT_OBJECT */ static void script_finalize(JSContext *cx, JSObject *obj) { JSScript *script; script = (JSScript *) JS_GetPrivate(cx, obj); if (script) js_DestroyScript(cx, script); } static JSBool script_call(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) { #if JS_HAS_SCRIPT_OBJECT return script_exec(cx, JSVAL_TO_OBJECT(argv[-2]), argc, argv, rval); #else return JS_FALSE; #endif } static uint32 script_mark(JSContext *cx, JSObject *obj, void *arg) { JSScript *script; script = (JSScript *) JS_GetPrivate(cx, obj); if (script) js_MarkScript(cx, script); return 0; } #if !JS_HAS_SCRIPT_OBJECT const char js_Script_str[] = "Script"; #define JSProto_Script JSProto_Object #endif JS_FRIEND_DATA(JSClass) js_ScriptClass = { js_Script_str, JSCLASS_HAS_PRIVATE | JSCLASS_HAS_CACHED_PROTO(JSProto_Script) | JSCLASS_HAS_RESERVED_SLOTS(1), JS_PropertyStub, JS_PropertyStub, JS_PropertyStub, JS_PropertyStub, JS_EnumerateStub, JS_ResolveStub, JS_ConvertStub, script_finalize, NULL, NULL, script_call, NULL,/*XXXbe xdr*/ NULL, NULL, script_mark, 0 }; #if JS_HAS_SCRIPT_OBJECT static JSBool Script(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) { /* If not constructing, replace obj with a new Script object. */ if (!(cx->fp->flags & JSFRAME_CONSTRUCTING)) { obj = js_NewObject(cx, &js_ScriptClass, NULL, NULL); if (!obj) return JS_FALSE; /* * script_compile does not use rval to root its temporaries * so we can use it to root obj. */ *rval = OBJECT_TO_JSVAL(obj); } if (!JS_SetReservedSlot(cx, obj, 0, INT_TO_JSVAL(0))) return JS_FALSE; return script_compile(cx, obj, argc, argv, rval); } #if JS_HAS_XDR_FREEZE_THAW static JSBool script_static_thaw(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) { obj = js_NewObject(cx, &js_ScriptClass, NULL, NULL); if (!obj) return JS_FALSE; if (!script_thaw(cx, obj, argc, argv, rval)) return JS_FALSE; *rval = OBJECT_TO_JSVAL(obj); return JS_TRUE; } static JSFunctionSpec script_static_methods[] = { {js_thaw_str, script_static_thaw, 1,0,0}, {0,0,0,0,0} }; #else /* !JS_HAS_XDR_FREEZE_THAW */ #define script_static_methods NULL #endif /* !JS_HAS_XDR_FREEZE_THAW */ JSObject * js_InitScriptClass(JSContext *cx, JSObject *obj) { return JS_InitClass(cx, obj, NULL, &js_ScriptClass, Script, 1, NULL, script_methods, NULL, script_static_methods); } #endif /* JS_HAS_SCRIPT_OBJECT */ /* * Shared script filename management. */ JS_STATIC_DLL_CALLBACK(int) js_compare_strings(const void *k1, const void *k2) { return strcmp(k1, k2) == 0; } /* Shared with jsatom.c to save code space. */ extern void * JS_DLL_CALLBACK js_alloc_table_space(void *priv, size_t size); extern void JS_DLL_CALLBACK js_free_table_space(void *priv, void *item); /* NB: This struct overlays JSHashEntry -- see jshash.h, do not reorganize. */ typedef struct ScriptFilenameEntry { JSHashEntry *next; /* hash chain linkage */ JSHashNumber keyHash; /* key hash function result */ const void *key; /* ptr to filename, below */ uint32 flags; /* user-defined filename prefix flags */ JSPackedBool mark; /* GC mark flag */ char filename[3]; /* two or more bytes, NUL-terminated */ } ScriptFilenameEntry; JS_STATIC_DLL_CALLBACK(JSHashEntry *) js_alloc_sftbl_entry(void *priv, const void *key) { size_t nbytes = offsetof(ScriptFilenameEntry, filename) + strlen(key) + 1; return (JSHashEntry *) malloc(JS_MAX(nbytes, sizeof(JSHashEntry))); } JS_STATIC_DLL_CALLBACK(void) js_free_sftbl_entry(void *priv, JSHashEntry *he, uintN flag) { if (flag != HT_FREE_ENTRY) return; free(he); } static JSHashAllocOps sftbl_alloc_ops = { js_alloc_table_space, js_free_table_space, js_alloc_sftbl_entry, js_free_sftbl_entry }; JSBool js_InitRuntimeScriptState(JSRuntime *rt) { #ifdef JS_THREADSAFE JS_ASSERT(!rt->scriptFilenameTableLock); rt->scriptFilenameTableLock = JS_NEW_LOCK(); if (!rt->scriptFilenameTableLock) return JS_FALSE; #endif JS_ASSERT(!rt->scriptFilenameTable); rt->scriptFilenameTable = JS_NewHashTable(16, JS_HashString, js_compare_strings, NULL, &sftbl_alloc_ops, NULL); if (!rt->scriptFilenameTable) { js_FinishRuntimeScriptState(rt); /* free lock if threadsafe */ return JS_FALSE; } JS_INIT_CLIST(&rt->scriptFilenamePrefixes); return JS_TRUE; } typedef struct ScriptFilenamePrefix { JSCList links; /* circular list linkage for easy deletion */ const char *name; /* pointer to pinned ScriptFilenameEntry string */ size_t length; /* prefix string length, precomputed */ uint32 flags; /* user-defined flags to inherit from this prefix */ } ScriptFilenamePrefix; void js_FinishRuntimeScriptState(JSRuntime *rt) { if (rt->scriptFilenameTable) { JS_HashTableDestroy(rt->scriptFilenameTable); rt->scriptFilenameTable = NULL; } #ifdef JS_THREADSAFE if (rt->scriptFilenameTableLock) { JS_DESTROY_LOCK(rt->scriptFilenameTableLock); rt->scriptFilenameTableLock = NULL; } #endif } void js_FreeRuntimeScriptState(JSRuntime *rt) { ScriptFilenamePrefix *sfp; if (!rt->scriptFilenameTable) return; while (!JS_CLIST_IS_EMPTY(&rt->scriptFilenamePrefixes)) { sfp = (ScriptFilenamePrefix *) rt->scriptFilenamePrefixes.next; JS_REMOVE_LINK(&sfp->links); free(sfp); } js_FinishRuntimeScriptState(rt); } #ifdef DEBUG_brendan #define DEBUG_SFTBL #endif #ifdef DEBUG_SFTBL size_t sftbl_savings = 0; #endif static ScriptFilenameEntry * SaveScriptFilename(JSRuntime *rt, const char *filename, uint32 flags) { JSHashTable *table; JSHashNumber hash; JSHashEntry **hep; ScriptFilenameEntry *sfe; size_t length; JSCList *head, *link; ScriptFilenamePrefix *sfp; table = rt->scriptFilenameTable; hash = JS_HashString(filename); hep = JS_HashTableRawLookup(table, hash, filename); sfe = (ScriptFilenameEntry *) *hep; #ifdef DEBUG_SFTBL if (sfe) sftbl_savings += strlen(sfe->filename); #endif if (!sfe) { sfe = (ScriptFilenameEntry *) JS_HashTableRawAdd(table, hep, hash, filename, NULL); if (!sfe) return NULL; sfe->key = strcpy(sfe->filename, filename); sfe->flags = 0; sfe->mark = JS_FALSE; } /* If saving a prefix, add it to the set in rt->scriptFilenamePrefixes. */ if (flags != 0) { /* Search in case filename was saved already; we must be idempotent. */ sfp = NULL; length = strlen(filename); for (head = link = &rt->scriptFilenamePrefixes; link->next != head; link = link->next) { /* Lag link behind sfp to insert in non-increasing length order. */ sfp = (ScriptFilenamePrefix *) link->next; if (!strcmp(sfp->name, filename)) break; if (sfp->length <= length) { sfp = NULL; break; } sfp = NULL; } if (!sfp) { /* No such prefix: add one now. */ sfp = (ScriptFilenamePrefix *) malloc(sizeof(ScriptFilenamePrefix)); if (!sfp) return NULL; JS_INSERT_AFTER(&sfp->links, link); sfp->name = sfe->filename; sfp->length = length; sfp->flags = 0; } /* * Accumulate flags in both sfe and sfp: sfe for later access from the * JS_GetScriptedCallerFilenameFlags debug-API, and sfp so that longer * filename entries can inherit by prefix. */ sfe->flags |= flags; sfp->flags |= flags; } return sfe; } const char * js_SaveScriptFilename(JSContext *cx, const char *filename) { JSRuntime *rt; ScriptFilenameEntry *sfe; JSCList *head, *link; ScriptFilenamePrefix *sfp; rt = cx->runtime; JS_ACQUIRE_LOCK(rt->scriptFilenameTableLock); sfe = SaveScriptFilename(rt, filename, 0); if (!sfe) { JS_RELEASE_LOCK(rt->scriptFilenameTableLock); JS_ReportOutOfMemory(cx); return NULL; } /* * Try to inherit flags by prefix. We assume there won't be more than a * few (dozen! ;-) prefixes, so linear search is tolerable. * XXXbe every time I've assumed that in the JS engine, I've been wrong! */ for (head = &rt->scriptFilenamePrefixes, link = head->next; link != head; link = link->next) { sfp = (ScriptFilenamePrefix *) link; if (!strncmp(sfp->name, filename, sfp->length)) { sfe->flags |= sfp->flags; break; } } JS_RELEASE_LOCK(rt->scriptFilenameTableLock); return sfe->filename; } const char * js_SaveScriptFilenameRT(JSRuntime *rt, const char *filename, uint32 flags) { ScriptFilenameEntry *sfe; /* This may be called very early, via the jsdbgapi.h entry point. */ if (!rt->scriptFilenameTable && !js_InitRuntimeScriptState(rt)) return NULL; JS_ACQUIRE_LOCK(rt->scriptFilenameTableLock); sfe = SaveScriptFilename(rt, filename, flags); JS_RELEASE_LOCK(rt->scriptFilenameTableLock); if (!sfe) return NULL; return sfe->filename; } /* * Back up from a saved filename by its offset within its hash table entry. */ #define FILENAME_TO_SFE(fn) \ ((ScriptFilenameEntry *) ((fn) - offsetof(ScriptFilenameEntry, filename))) /* * The sfe->key member, redundant given sfe->filename but required by the old * jshash.c code, here gives us a useful sanity check. This assertion will * very likely botch if someone tries to mark a string that wasn't allocated * as an sfe->filename. */ #define ASSERT_VALID_SFE(sfe) JS_ASSERT((sfe)->key == (sfe)->filename) uint32 js_GetScriptFilenameFlags(const char *filename) { ScriptFilenameEntry *sfe; sfe = FILENAME_TO_SFE(filename); ASSERT_VALID_SFE(sfe); return sfe->flags; } void js_MarkScriptFilename(const char *filename) { ScriptFilenameEntry *sfe; sfe = FILENAME_TO_SFE(filename); ASSERT_VALID_SFE(sfe); sfe->mark = JS_TRUE; } JS_STATIC_DLL_CALLBACK(intN) js_script_filename_marker(JSHashEntry *he, intN i, void *arg) { ScriptFilenameEntry *sfe = (ScriptFilenameEntry *) he; sfe->mark = JS_TRUE; return HT_ENUMERATE_NEXT; } void js_MarkScriptFilenames(JSRuntime *rt, JSBool keepAtoms) { JSCList *head, *link; ScriptFilenamePrefix *sfp; if (!rt->scriptFilenameTable) return; if (keepAtoms) { JS_HashTableEnumerateEntries(rt->scriptFilenameTable, js_script_filename_marker, rt); } for (head = &rt->scriptFilenamePrefixes, link = head->next; link != head; link = link->next) { sfp = (ScriptFilenamePrefix *) link; js_MarkScriptFilename(sfp->name); } } JS_STATIC_DLL_CALLBACK(intN) js_script_filename_sweeper(JSHashEntry *he, intN i, void *arg) { ScriptFilenameEntry *sfe = (ScriptFilenameEntry *) he; if (!sfe->mark) return HT_ENUMERATE_REMOVE; sfe->mark = JS_FALSE; return HT_ENUMERATE_NEXT; } void js_SweepScriptFilenames(JSRuntime *rt) { if (!rt->scriptFilenameTable) return; JS_HashTableEnumerateEntries(rt->scriptFilenameTable, js_script_filename_sweeper, rt); #ifdef DEBUG_notme #ifdef DEBUG_SFTBL printf("script filename table savings so far: %u\n", sftbl_savings); #endif #endif } JSScript * js_NewScript(JSContext *cx, uint32 length, uint32 nsrcnotes, uint32 ntrynotes) { JSScript *script; /* Round up source note count to align script->trynotes for its type. */ if (ntrynotes) nsrcnotes += JSTRYNOTE_ALIGNMASK; script = (JSScript *) JS_malloc(cx, sizeof(JSScript) + length * sizeof(jsbytecode) + nsrcnotes * sizeof(jssrcnote) + ntrynotes * sizeof(JSTryNote)); if (!script) return NULL; memset(script, 0, sizeof(JSScript)); script->code = script->main = (jsbytecode *)(script + 1); script->length = length; script->version = cx->version; if (ntrynotes) { script->trynotes = (JSTryNote *) ((jsword)(SCRIPT_NOTES(script) + nsrcnotes) & ~(jsword)JSTRYNOTE_ALIGNMASK); memset(script->trynotes, 0, ntrynotes * sizeof(JSTryNote)); } return script; } JS_FRIEND_API(JSScript *) js_NewScriptFromCG(JSContext *cx, JSCodeGenerator *cg, JSFunction *fun) { uint32 mainLength, prologLength, nsrcnotes, ntrynotes; JSScript *script; const char *filename; mainLength = CG_OFFSET(cg); prologLength = CG_PROLOG_OFFSET(cg); CG_COUNT_FINAL_SRCNOTES(cg, nsrcnotes); CG_COUNT_FINAL_TRYNOTES(cg, ntrynotes); script = js_NewScript(cx, prologLength + mainLength, nsrcnotes, ntrynotes); if (!script) return NULL; /* Now that we have script, error control flow must go to label bad. */ script->main += prologLength; memcpy(script->code, CG_PROLOG_BASE(cg), prologLength * sizeof(jsbytecode)); memcpy(script->main, CG_BASE(cg), mainLength * sizeof(jsbytecode)); script->numGlobalVars = cg->treeContext.numGlobalVars; if (!js_InitAtomMap(cx, &script->atomMap, &cg->atomList)) goto bad; filename = cg->filename; if (filename) { script->filename = js_SaveScriptFilename(cx, filename); if (!script->filename) goto bad; } script->lineno = cg->firstLine; script->depth = cg->maxStackDepth; if (cg->principals) { script->principals = cg->principals; JSPRINCIPALS_HOLD(cx, script->principals); } if (!js_FinishTakingSrcNotes(cx, cg, SCRIPT_NOTES(script))) goto bad; if (script->trynotes) js_FinishTakingTryNotes(cx, cg, script->trynotes); /* * We initialize fun->u.script to be the script constructed above * so that the debugger has a valid FUN_SCRIPT(fun). */ if (fun) { JS_ASSERT(FUN_INTERPRETED(fun) && !FUN_SCRIPT(fun)); fun->u.i.script = script; if (cg->treeContext.flags & TCF_FUN_HEAVYWEIGHT) fun->flags |= JSFUN_HEAVYWEIGHT; } /* Tell the debugger about this compiled script. */ js_CallNewScriptHook(cx, script, fun); return script; bad: js_DestroyScript(cx, script); return NULL; } JS_FRIEND_API(void) js_CallNewScriptHook(JSContext *cx, JSScript *script, JSFunction *fun) { JSRuntime *rt; JSNewScriptHook hook; rt = cx->runtime; hook = rt->newScriptHook; if (hook) { JS_KEEP_ATOMS(rt); hook(cx, script->filename, script->lineno, script, fun, rt->newScriptHookData); JS_UNKEEP_ATOMS(rt); } } JS_FRIEND_API(void) js_CallDestroyScriptHook(JSContext *cx, JSScript *script) { JSRuntime *rt; JSDestroyScriptHook hook; rt = cx->runtime; hook = rt->destroyScriptHook; if (hook) hook(cx, script, rt->destroyScriptHookData); } void js_DestroyScript(JSContext *cx, JSScript *script) { js_CallDestroyScriptHook(cx, script); JS_ClearScriptTraps(cx, script); js_FreeAtomMap(cx, &script->atomMap); if (script->principals) JSPRINCIPALS_DROP(cx, script->principals); if (JS_GSN_CACHE(cx).script == script) JS_CLEAR_GSN_CACHE(cx); JS_free(cx, script); } void js_MarkScript(JSContext *cx, JSScript *script) { JSAtomMap *map; uintN i, length; JSAtom **vector; map = &script->atomMap; length = map->length; vector = map->vector; for (i = 0; i < length; i++) GC_MARK_ATOM(cx, vector[i]); if (script->filename) js_MarkScriptFilename(script->filename); } typedef struct GSNCacheEntry { JSDHashEntryHdr hdr; jsbytecode *pc; jssrcnote *sn; } GSNCacheEntry; #define GSN_CACHE_THRESHOLD 100 jssrcnote * js_GetSrcNoteCached(JSContext *cx, JSScript *script, jsbytecode *pc) { ptrdiff_t target, offset; GSNCacheEntry *entry; jssrcnote *sn, *result; uintN nsrcnotes; target = PTRDIFF(pc, script->code, jsbytecode); if ((uint32)target >= script->length) return NULL; if (JS_GSN_CACHE(cx).script == script) { JS_METER_GSN_CACHE(cx, hits); entry = (GSNCacheEntry *) JS_DHashTableOperate(&JS_GSN_CACHE(cx).table, pc, JS_DHASH_LOOKUP); return entry->sn; } JS_METER_GSN_CACHE(cx, misses); offset = 0; for (sn = SCRIPT_NOTES(script); ; sn = SN_NEXT(sn)) { if (SN_IS_TERMINATOR(sn)) { result = NULL; break; } offset += SN_DELTA(sn); if (offset == target && SN_IS_GETTABLE(sn)) { result = sn; break; } } if (JS_GSN_CACHE(cx).script != script && script->length >= GSN_CACHE_THRESHOLD) { JS_CLEAR_GSN_CACHE(cx); nsrcnotes = 0; for (sn = SCRIPT_NOTES(script); !SN_IS_TERMINATOR(sn); sn = SN_NEXT(sn)) { if (SN_IS_GETTABLE(sn)) ++nsrcnotes; } if (!JS_DHashTableInit(&JS_GSN_CACHE(cx).table, JS_DHashGetStubOps(), NULL, sizeof(GSNCacheEntry), nsrcnotes)) { JS_GSN_CACHE(cx).table.ops = NULL; } else { pc = script->code; for (sn = SCRIPT_NOTES(script); !SN_IS_TERMINATOR(sn); sn = SN_NEXT(sn)) { pc += SN_DELTA(sn); if (SN_IS_GETTABLE(sn)) { entry = (GSNCacheEntry *) JS_DHashTableOperate(&JS_GSN_CACHE(cx).table, pc, JS_DHASH_ADD); entry->pc = pc; entry->sn = sn; } } JS_GSN_CACHE(cx).script = script; JS_METER_GSN_CACHE(cx, fills); } } return result; } uintN js_PCToLineNumber(JSContext *cx, JSScript *script, jsbytecode *pc) { JSAtom *atom; JSFunction *fun; uintN lineno; ptrdiff_t offset, target; jssrcnote *sn; JSSrcNoteType type; /* Cope with JSStackFrame.pc value prior to entering js_Interpret. */ if (!pc) return 0; /* * Special case: function definition needs no line number note because * the function's script contains its starting line number. */ if (*pc == JSOP_DEFFUN || (*pc == JSOP_LITOPX && pc[1 + LITERAL_INDEX_LEN] == JSOP_DEFFUN)) { atom = js_GetAtom(cx, &script->atomMap, (*pc == JSOP_DEFFUN) ? GET_ATOM_INDEX(pc) : GET_LITERAL_INDEX(pc)); fun = (JSFunction *) JS_GetPrivate(cx, ATOM_TO_OBJECT(atom)); JS_ASSERT(FUN_INTERPRETED(fun)); return fun->u.i.script->lineno; } /* * General case: walk through source notes accumulating their deltas, * keeping track of line-number notes, until we pass the note for pc's * offset within script->code. */ lineno = script->lineno; offset = 0; target = PTRDIFF(pc, script->code, jsbytecode); for (sn = SCRIPT_NOTES(script); !SN_IS_TERMINATOR(sn); sn = SN_NEXT(sn)) { offset += SN_DELTA(sn); type = (JSSrcNoteType) SN_TYPE(sn); if (type == SRC_SETLINE) { if (offset <= target) lineno = (uintN) js_GetSrcNoteOffset(sn, 0); } else if (type == SRC_NEWLINE) { if (offset <= target) lineno++; } if (offset > target) break; } return lineno; } /* The line number limit is the same as the jssrcnote offset limit. */ #define SN_LINE_LIMIT (SN_3BYTE_OFFSET_FLAG << 16) jsbytecode * js_LineNumberToPC(JSScript *script, uintN target) { ptrdiff_t offset, best; uintN lineno, bestdiff, diff; jssrcnote *sn; JSSrcNoteType type; offset = 0; best = -1; lineno = script->lineno; bestdiff = SN_LINE_LIMIT; for (sn = SCRIPT_NOTES(script); !SN_IS_TERMINATOR(sn); sn = SN_NEXT(sn)) { if (lineno == target) goto out; if (lineno > target) { diff = lineno - target; if (diff < bestdiff) { bestdiff = diff; best = offset; } } offset += SN_DELTA(sn); type = (JSSrcNoteType) SN_TYPE(sn); if (type == SRC_SETLINE) { lineno = (uintN) js_GetSrcNoteOffset(sn, 0); } else if (type == SRC_NEWLINE) { lineno++; } } if (best >= 0) offset = best; out: return script->code + offset; } JS_FRIEND_API(uintN) js_GetScriptLineExtent(JSScript *script) { uintN lineno; jssrcnote *sn; JSSrcNoteType type; lineno = script->lineno; for (sn = SCRIPT_NOTES(script); !SN_IS_TERMINATOR(sn); sn = SN_NEXT(sn)) { type = (JSSrcNoteType) SN_TYPE(sn); if (type == SRC_SETLINE) { lineno = (uintN) js_GetSrcNoteOffset(sn, 0); } else if (type == SRC_NEWLINE) { lineno++; } } return 1 + lineno - script->lineno; } #if JS_HAS_GENERATORS jsbytecode * js_FindFinallyHandler(JSScript *script, jsbytecode *pc) { JSTryNote *tn; ptrdiff_t off; JSOp op2; tn = script->trynotes; if (!tn) return NULL; off = pc - script->main; if (off < 0) return NULL; JS_ASSERT(tn->catchStart != 0); do { if ((jsuword)(off - tn->start) < (jsuword)tn->length) { /* * We have a handler: is it the finally one, or a catch handler? * * Catch bytecode begins with: JSOP_SETSP JSOP_ENTERBLOCK * Finally bytecode begins with: JSOP_SETSP JSOP_(GOSUB|EXCEPTION) */ pc = script->main + tn->catchStart; JS_ASSERT(*pc == JSOP_SETSP); op2 = pc[JSOP_SETSP_LENGTH]; if (op2 != JSOP_ENTERBLOCK) { JS_ASSERT(op2 == JSOP_GOSUB || op2 == JSOP_EXCEPTION); return pc; } } } while ((++tn)->catchStart != 0); return NULL; } #endif pacparser-1.4.5/src/spidermonkey/js/src/jsscript.h000066400000000000000000000212421464010763600222260ustar00rootroot00000000000000/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- * vim: set ts=8 sw=4 et tw=78: * * ***** BEGIN LICENSE BLOCK ***** * Version: MPL 1.1/GPL 2.0/LGPL 2.1 * * The contents of this file are subject to the Mozilla Public License Version * 1.1 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * http://www.mozilla.org/MPL/ * * Software distributed under the License is distributed on an "AS IS" basis, * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License * for the specific language governing rights and limitations under the * License. * * The Original Code is Mozilla Communicator client code, released * March 31, 1998. * * The Initial Developer of the Original Code is * Netscape Communications Corporation. * Portions created by the Initial Developer are Copyright (C) 1998 * the Initial Developer. All Rights Reserved. * * Contributor(s): * * Alternatively, the contents of this file may be used under the terms of * either of the GNU General Public License Version 2 or later (the "GPL"), * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), * in which case the provisions of the GPL or the LGPL are applicable instead * of those above. If you wish to allow use of your version of this file only * under the terms of either the GPL or the LGPL, and not to allow others to * use your version of this file under the terms of the MPL, indicate your * decision by deleting the provisions above and replace them with the notice * and other provisions required by the GPL or the LGPL. If you do not delete * the provisions above, a recipient may use your version of this file under * the terms of any one of the MPL, the GPL or the LGPL. * * ***** END LICENSE BLOCK ***** */ #ifndef jsscript_h___ #define jsscript_h___ /* * JS script descriptor. */ #include "jsatom.h" #include "jsprvtd.h" JS_BEGIN_EXTERN_C /* * Exception handling runtime information. * * All fields except length are code offsets relative to the main entry point * of the script. If script->trynotes is not null, it points to a vector of * these structs terminated by one with catchStart == 0. */ struct JSTryNote { ptrdiff_t start; /* start of try statement */ ptrdiff_t length; /* count of try statement bytecodes */ ptrdiff_t catchStart; /* start of catch block (0 if end) */ }; #define JSTRYNOTE_GRAIN sizeof(ptrdiff_t) #define JSTRYNOTE_ALIGNMASK (JSTRYNOTE_GRAIN - 1) struct JSScript { jsbytecode *code; /* bytecodes and their immediate operands */ uint32 length; /* length of code vector */ jsbytecode *main; /* main entry point, after predef'ing prolog */ uint16 version; /* JS version under which script was compiled */ uint16 numGlobalVars; /* declared global var/const/function count */ JSAtomMap atomMap; /* maps immediate index to literal struct */ const char *filename; /* source filename or null */ uintN lineno; /* base line number of script */ uintN depth; /* maximum stack depth in slots */ JSTryNote *trynotes; /* exception table for this script */ JSPrincipals *principals; /* principals for this script */ JSObject *object; /* optional Script-class object wrapper */ }; /* No need to store script->notes now that it is allocated right after code. */ #define SCRIPT_NOTES(script) ((jssrcnote*)((script)->code+(script)->length)) #define SCRIPT_FIND_CATCH_START(script, pc, catchpc) \ JS_BEGIN_MACRO \ JSTryNote *tn_ = (script)->trynotes; \ jsbytecode *catchpc_ = NULL; \ if (tn_) { \ ptrdiff_t off_ = PTRDIFF(pc, (script)->main, jsbytecode); \ if (off_ >= 0) { \ while ((jsuword)(off_ - tn_->start) >= (jsuword)tn_->length) \ ++tn_; \ if (tn_->catchStart) \ catchpc_ = (script)->main + tn_->catchStart; \ } \ } \ catchpc = catchpc_; \ JS_END_MACRO /* * Find the innermost finally block that handles the given pc. This is a * version of SCRIPT_FIND_CATCH_START that ignore catch blocks and is used * to implement generator.close(). */ jsbytecode * js_FindFinallyHandler(JSScript *script, jsbytecode *pc); extern JS_FRIEND_DATA(JSClass) js_ScriptClass; extern JSObject * js_InitScriptClass(JSContext *cx, JSObject *obj); /* * On first new context in rt, initialize script runtime state, specifically * the script filename table and its lock. */ extern JSBool js_InitRuntimeScriptState(JSRuntime *rt); /* * On last context destroy for rt, if script filenames are all GC'd, free the * script filename table and its lock. */ extern void js_FinishRuntimeScriptState(JSRuntime *rt); /* * On JS_DestroyRuntime(rt), forcibly free script filename prefixes and any * script filename table entries that have not been GC'd, the latter using * js_FinishRuntimeScriptState. * * This allows script filename prefixes to outlive any context in rt. */ extern void js_FreeRuntimeScriptState(JSRuntime *rt); extern const char * js_SaveScriptFilename(JSContext *cx, const char *filename); extern const char * js_SaveScriptFilenameRT(JSRuntime *rt, const char *filename, uint32 flags); extern uint32 js_GetScriptFilenameFlags(const char *filename); extern void js_MarkScriptFilename(const char *filename); extern void js_MarkScriptFilenames(JSRuntime *rt, JSBool keepAtoms); extern void js_SweepScriptFilenames(JSRuntime *rt); /* * Two successively less primitive ways to make a new JSScript. The first * does *not* call a non-null cx->runtime->newScriptHook -- only the second, * js_NewScriptFromCG, calls this optional debugger hook. * * The js_NewScript function can't know whether the script it creates belongs * to a function, or is top-level or eval code, but the debugger wants access * to the newly made script's function, if any -- so callers of js_NewScript * are responsible for notifying the debugger after successfully creating any * kind (function or other) of new JSScript. */ extern JSScript * js_NewScript(JSContext *cx, uint32 length, uint32 snlength, uint32 tnlength); extern JS_FRIEND_API(JSScript *) js_NewScriptFromCG(JSContext *cx, JSCodeGenerator *cg, JSFunction *fun); /* * New-script-hook calling is factored from js_NewScriptFromCG so that it * and callers of js_XDRScript can share this code. In the case of callers * of js_XDRScript, the hook should be invoked only after successful decode * of any owning function (the fun parameter) or script object (null fun). */ extern JS_FRIEND_API(void) js_CallNewScriptHook(JSContext *cx, JSScript *script, JSFunction *fun); extern JS_FRIEND_API(void) js_CallDestroyScriptHook(JSContext *cx, JSScript *script); extern void js_DestroyScript(JSContext *cx, JSScript *script); extern void js_MarkScript(JSContext *cx, JSScript *script); /* * To perturb as little code as possible, we introduce a js_GetSrcNote lookup * cache without adding an explicit cx parameter. Thus js_GetSrcNote becomes * a macro that uses cx from its calls' lexical environments. */ #define js_GetSrcNote(script,pc) js_GetSrcNoteCached(cx, script, pc) extern jssrcnote * js_GetSrcNoteCached(JSContext *cx, JSScript *script, jsbytecode *pc); /* XXX need cx to lock function objects declared by prolog bytecodes. */ extern uintN js_PCToLineNumber(JSContext *cx, JSScript *script, jsbytecode *pc); extern jsbytecode * js_LineNumberToPC(JSScript *script, uintN lineno); extern JS_FRIEND_API(uintN) js_GetScriptLineExtent(JSScript *script); /* * If magic is non-null, js_XDRScript succeeds on magic number mismatch but * returns false in *magic; it reflects a match via a true *magic out param. * If magic is null, js_XDRScript returns false on bad magic number errors, * which it reports. * * NB: callers must call js_CallNewScriptHook after successful JSXDR_DECODE * and subsequent set-up of owning function or script object, if any. */ extern JSBool js_XDRScript(JSXDRState *xdr, JSScript **scriptp, JSBool *magic); JS_END_EXTERN_C #endif /* jsscript_h___ */ pacparser-1.4.5/src/spidermonkey/js/src/jsshell.msg000066400000000000000000000047101464010763600223710ustar00rootroot00000000000000/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- * * ***** BEGIN LICENSE BLOCK ***** * Version: MPL 1.1/GPL 2.0/LGPL 2.1 * * The contents of this file are subject to the Mozilla Public License Version * 1.1 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * http://www.mozilla.org/MPL/ * * Software distributed under the License is distributed on an "AS IS" basis, * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License * for the specific language governing rights and limitations under the * License. * * The Original Code is Mozilla Communicator client code, released * March 31, 1998. * * The Initial Developer of the Original Code is * Netscape Communications Corporation. * Portions created by the Initial Developer are Copyright (C) 1998 * the Initial Developer. All Rights Reserved. * * Contributor(s): * * Alternatively, the contents of this file may be used under the terms of * either of the GNU General Public License Version 2 or later (the "GPL"), * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), * in which case the provisions of the GPL or the LGPL are applicable instead * of those above. If you wish to allow use of your version of this file only * under the terms of either the GPL or the LGPL, and not to allow others to * use your version of this file under the terms of the MPL, indicate your * decision by deleting the provisions above and replace them with the notice * and other provisions required by the GPL or the LGPL. If you do not delete * the provisions above, a recipient may use your version of this file under * the terms of any one of the MPL, the GPL or the LGPL. * * ***** END LICENSE BLOCK ***** */ /* Error messages for JSShell. See js.msg for format. */ MSG_DEF(JSSMSG_NOT_AN_ERROR, 0, 0, JSEXN_NONE, "") MSG_DEF(JSSMSG_CANT_OPEN, 1, 2, JSEXN_NONE, "can't open {0}: {1}") MSG_DEF(JSSMSG_TRAP_USAGE, 2, 0, JSEXN_NONE, "usage: trap [fun] [pc] expr") MSG_DEF(JSSMSG_LINE2PC_USAGE, 3, 0, JSEXN_NONE, "usage: line2pc [fun] line") MSG_DEF(JSSMSG_FILE_SCRIPTS_ONLY, 4, 0, JSEXN_NONE, "only works on JS scripts read from files") MSG_DEF(JSSMSG_UNEXPECTED_EOF, 5, 1, JSEXN_NONE, "unexpected EOF in {0}") MSG_DEF(JSSMSG_DOEXP_USAGE, 6, 0, JSEXN_NONE, "usage: doexp obj id") pacparser-1.4.5/src/spidermonkey/js/src/jsstddef.h000066400000000000000000000052071464010763600221760ustar00rootroot00000000000000/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- * * ***** BEGIN LICENSE BLOCK ***** * Version: MPL 1.1/GPL 2.0/LGPL 2.1 * * The contents of this file are subject to the Mozilla Public License Version * 1.1 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * http://www.mozilla.org/MPL/ * * Software distributed under the License is distributed on an "AS IS" basis, * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License * for the specific language governing rights and limitations under the * License. * * The Original Code is Mozilla Communicator client code, released * March 31, 1998. * * The Initial Developer of the Original Code is * Netscape Communications Corporation. * Portions created by the Initial Developer are Copyright (C) 1998 * the Initial Developer. All Rights Reserved. * * Contributor(s): * * Alternatively, the contents of this file may be used under the terms of * either of the GNU General Public License Version 2 or later (the "GPL"), * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), * in which case the provisions of the GPL or the LGPL are applicable instead * of those above. If you wish to allow use of your version of this file only * under the terms of either the GPL or the LGPL, and not to allow others to * use your version of this file under the terms of the MPL, indicate your * decision by deleting the provisions above and replace them with the notice * and other provisions required by the GPL or the LGPL. If you do not delete * the provisions above, a recipient may use your version of this file under * the terms of any one of the MPL, the GPL or the LGPL. * * ***** END LICENSE BLOCK ***** */ /* * stddef inclusion here to first declare ptrdif as a signed long instead of a * signed int. */ #ifdef _WINDOWS # ifndef XP_WIN # define XP_WIN # endif #if defined(_WIN32) || defined(WIN32) # ifndef XP_WIN32 # define XP_WIN32 # endif #else # ifndef XP_WIN16 # define XP_WIN16 # endif #endif #endif #ifdef XP_WIN16 #ifndef _PTRDIFF_T_DEFINED typedef long ptrdiff_t; /* * The Win16 compiler treats pointer differences as 16-bit signed values. * This macro allows us to treat them as 17-bit signed values, stored in * a 32-bit type. */ #define PTRDIFF(p1, p2, type) \ ((((unsigned long)(p1)) - ((unsigned long)(p2))) / sizeof(type)) #define _PTRDIFF_T_DEFINED #endif /*_PTRDIFF_T_DEFINED*/ #else /*WIN16*/ #define PTRDIFF(p1, p2, type) \ ((p1) - (p2)) #endif #include pacparser-1.4.5/src/spidermonkey/js/src/jsstr.c000066400000000000000000005200661464010763600215350ustar00rootroot00000000000000/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- * vim: set ts=8 sw=4 et tw=80: * * ***** BEGIN LICENSE BLOCK ***** * Version: MPL 1.1/GPL 2.0/LGPL 2.1 * * The contents of this file are subject to the Mozilla Public License Version * 1.1 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * http://www.mozilla.org/MPL/ * * Software distributed under the License is distributed on an "AS IS" basis, * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License * for the specific language governing rights and limitations under the * License. * * The Original Code is Mozilla Communicator client code, released * March 31, 1998. * * The Initial Developer of the Original Code is * Netscape Communications Corporation. * Portions created by the Initial Developer are Copyright (C) 1998 * the Initial Developer. All Rights Reserved. * * Contributor(s): * * Alternatively, the contents of this file may be used under the terms of * either of the GNU General Public License Version 2 or later (the "GPL"), * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), * in which case the provisions of the GPL or the LGPL are applicable instead * of those above. If you wish to allow use of your version of this file only * under the terms of either the GPL or the LGPL, and not to allow others to * use your version of this file under the terms of the MPL, indicate your * decision by deleting the provisions above and replace them with the notice * and other provisions required by the GPL or the LGPL. If you do not delete * the provisions above, a recipient may use your version of this file under * the terms of any one of the MPL, the GPL or the LGPL. * * ***** END LICENSE BLOCK ***** */ /* * JS string type implementation. * * In order to avoid unnecessary js_LockGCThing/js_UnlockGCThing calls, these * native methods store strings (possibly newborn) converted from their 'this' * parameter and arguments on the stack: 'this' conversions at argv[-1], arg * conversions at their index (argv[0], argv[1]). This is a legitimate method * of rooting things that might lose their newborn root due to subsequent GC * allocations in the same native method. */ #include "jsstddef.h" #include #include #include "jstypes.h" #include "jsutil.h" /* Added by JSIFY */ #include "jshash.h" /* Added by JSIFY */ #include "jsprf.h" #include "jsapi.h" #include "jsarray.h" #include "jsatom.h" #include "jsbool.h" #include "jscntxt.h" #include "jsconfig.h" #include "jsgc.h" #include "jsinterp.h" #include "jslock.h" #include "jsnum.h" #include "jsobj.h" #include "jsopcode.h" #include "jsregexp.h" #include "jsstr.h" #define JSSTRDEP_RECURSION_LIMIT 100 size_t js_MinimizeDependentStrings(JSString *str, int level, JSString **basep) { JSString *base; size_t start, length; JS_ASSERT(JSSTRING_IS_DEPENDENT(str)); base = JSSTRDEP_BASE(str); start = JSSTRDEP_START(str); if (JSSTRING_IS_DEPENDENT(base)) { if (level < JSSTRDEP_RECURSION_LIMIT) { start += js_MinimizeDependentStrings(base, level + 1, &base); } else { do { start += JSSTRDEP_START(base); base = JSSTRDEP_BASE(base); } while (JSSTRING_IS_DEPENDENT(base)); } if (start == 0) { JS_ASSERT(JSSTRING_IS_PREFIX(str)); JSPREFIX_SET_BASE(str, base); } else if (start <= JSSTRDEP_START_MASK) { length = JSSTRDEP_LENGTH(str); JSSTRDEP_SET_START_AND_LENGTH(str, start, length); JSSTRDEP_SET_BASE(str, base); } } *basep = base; return start; } jschar * js_GetDependentStringChars(JSString *str) { size_t start; JSString *base; start = js_MinimizeDependentStrings(str, 0, &base); JS_ASSERT(!JSSTRING_IS_DEPENDENT(base)); JS_ASSERT(start < base->length); return base->chars + start; } jschar * js_GetStringChars(JSString *str) { if (JSSTRING_IS_DEPENDENT(str) && !js_UndependString(NULL, str)) return NULL; *js_GetGCThingFlags(str) &= ~GCF_MUTABLE; return str->chars; } JSString * js_ConcatStrings(JSContext *cx, JSString *left, JSString *right) { size_t rn, ln, lrdist, n; jschar *rs, *ls, *s; JSDependentString *ldep; /* non-null if left should become dependent */ JSString *str; if (JSSTRING_IS_DEPENDENT(right)) { rn = JSSTRDEP_LENGTH(right); rs = JSSTRDEP_CHARS(right); } else { rn = right->length; rs = right->chars; } if (rn == 0) return left; if (JSSTRING_IS_DEPENDENT(left) || !(*js_GetGCThingFlags(left) & GCF_MUTABLE)) { /* We must copy if left does not own a buffer to realloc. */ ln = JSSTRING_LENGTH(left); if (ln == 0) return right; ls = JSSTRING_CHARS(left); s = (jschar *) JS_malloc(cx, (ln + rn + 1) * sizeof(jschar)); if (!s) return NULL; js_strncpy(s, ls, ln); ldep = NULL; } else { /* We can realloc left's space and make it depend on our result. */ ln = left->length; if (ln == 0) return right; ls = left->chars; s = (jschar *) JS_realloc(cx, ls, (ln + rn + 1) * sizeof(jschar)); if (!s) return NULL; /* Take care: right could depend on left! */ lrdist = (size_t)(rs - ls); if (lrdist < ln) rs = s + lrdist; left->chars = ls = s; ldep = JSSTRDEP(left); } js_strncpy(s + ln, rs, rn); n = ln + rn; s[n] = 0; str = js_NewString(cx, s, n, GCF_MUTABLE); if (!str) { /* Out of memory: clean up any space we (re-)allocated. */ if (!ldep) { JS_free(cx, s); } else { s = JS_realloc(cx, ls, (ln + 1) * sizeof(jschar)); if (s) left->chars = s; } } else { /* Morph left into a dependent prefix if we realloc'd its buffer. */ if (ldep) { JSPREFIX_SET_LENGTH(ldep, ln); JSPREFIX_SET_BASE(ldep, str); #ifdef DEBUG { JSRuntime *rt = cx->runtime; JS_RUNTIME_METER(rt, liveDependentStrings); JS_RUNTIME_METER(rt, totalDependentStrings); JS_LOCK_RUNTIME_VOID(rt, (rt->strdepLengthSum += (double)ln, rt->strdepLengthSquaredSum += (double)ln * (double)ln)); } #endif } } return str; } /* * May be called with null cx by js_GetStringChars, above; and by the jslock.c * MAKE_STRING_IMMUTABLE file-local macro. */ const jschar * js_UndependString(JSContext *cx, JSString *str) { size_t n, size; jschar *s; if (JSSTRING_IS_DEPENDENT(str)) { n = JSSTRDEP_LENGTH(str); size = (n + 1) * sizeof(jschar); s = (jschar *) (cx ? JS_malloc(cx, size) : malloc(size)); if (!s) return NULL; js_strncpy(s, JSSTRDEP_CHARS(str), n); s[n] = 0; str->length = n; str->chars = s; #ifdef DEBUG if (cx) { JSRuntime *rt = cx->runtime; JS_RUNTIME_UNMETER(rt, liveDependentStrings); JS_RUNTIME_UNMETER(rt, totalDependentStrings); JS_LOCK_RUNTIME_VOID(rt, (rt->strdepLengthSum -= (double)n, rt->strdepLengthSquaredSum -= (double)n * (double)n)); } #endif } return str->chars; } /* * Forward declarations for URI encode/decode and helper routines */ static JSBool str_decodeURI(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval); static JSBool str_decodeURI_Component(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval); static JSBool str_encodeURI(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval); static JSBool str_encodeURI_Component(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval); static uint32 Utf8ToOneUcs4Char(const uint8 *utf8Buffer, int utf8Length); /* * Contributions from the String class to the set of methods defined for the * global object. escape and unescape used to be defined in the Mocha library, * but as ECMA decided to spec them, they've been moved to the core engine * and made ECMA-compliant. (Incomplete escapes are interpreted as literal * characters by unescape.) */ /* * Stuff to emulate the old libmocha escape, which took a second argument * giving the type of escape to perform. Retained for compatibility, and * copied here to avoid reliance on net.h, mkparse.c/NET_EscapeBytes. */ #define URL_XALPHAS ((uint8) 1) #define URL_XPALPHAS ((uint8) 2) #define URL_PATH ((uint8) 4) static const uint8 urlCharType[256] = /* Bit 0 xalpha -- the alphas * Bit 1 xpalpha -- as xalpha but * converts spaces to plus and plus to %20 * Bit 2 ... path -- as xalphas but doesn't escape '/' */ /* 0 1 2 3 4 5 6 7 8 9 A B C D E F */ { 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* 0x */ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* 1x */ 0,0,0,0,0,0,0,0,0,0,7,4,0,7,7,4, /* 2x !"#$%&'()*+,-./ */ 7,7,7,7,7,7,7,7,7,7,0,0,0,0,0,0, /* 3x 0123456789:;<=>? */ 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, /* 4x @ABCDEFGHIJKLMNO */ 7,7,7,7,7,7,7,7,7,7,7,0,0,0,0,7, /* 5X PQRSTUVWXYZ[\]^_ */ 0,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, /* 6x `abcdefghijklmno */ 7,7,7,7,7,7,7,7,7,7,7,0,0,0,0,0, /* 7X pqrstuvwxyz{\}~ DEL */ 0, }; /* This matches the ECMA escape set when mask is 7 (default.) */ #define IS_OK(C, mask) (urlCharType[((uint8) (C))] & (mask)) /* See ECMA-262 15.1.2.4. */ JSBool js_str_escape(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) { JSString *str; size_t i, ni, length, newlength; const jschar *chars; jschar *newchars; jschar ch; jsint mask; jsdouble d; const char digits[] = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F' }; mask = URL_XALPHAS | URL_XPALPHAS | URL_PATH; if (argc > 1) { if (!js_ValueToNumber(cx, argv[1], &d)) return JS_FALSE; if (!JSDOUBLE_IS_FINITE(d) || (mask = (jsint)d) != d || mask & ~(URL_XALPHAS | URL_XPALPHAS | URL_PATH)) { char numBuf[12]; JS_snprintf(numBuf, sizeof numBuf, "%lx", (unsigned long) mask); JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_BAD_STRING_MASK, numBuf); return JS_FALSE; } } str = js_ValueToString(cx, argv[0]); if (!str) return JS_FALSE; argv[0] = STRING_TO_JSVAL(str); chars = JSSTRING_CHARS(str); length = newlength = JSSTRING_LENGTH(str); /* Take a first pass and see how big the result string will need to be. */ for (i = 0; i < length; i++) { if ((ch = chars[i]) < 128 && IS_OK(ch, mask)) continue; if (ch < 256) { if (mask == URL_XPALPHAS && ch == ' ') continue; /* The character will be encoded as '+' */ newlength += 2; /* The character will be encoded as %XX */ } else { newlength += 5; /* The character will be encoded as %uXXXX */ } /* * This overflow test works because newlength is incremented by at * most 5 on each iteration. */ if (newlength < length) { JS_ReportOutOfMemory(cx); return JS_FALSE; } } if (newlength >= ~(size_t)0 / sizeof(jschar)) { JS_ReportOutOfMemory(cx); return JS_FALSE; } newchars = (jschar *) JS_malloc(cx, (newlength + 1) * sizeof(jschar)); if (!newchars) return JS_FALSE; for (i = 0, ni = 0; i < length; i++) { if ((ch = chars[i]) < 128 && IS_OK(ch, mask)) { newchars[ni++] = ch; } else if (ch < 256) { if (mask == URL_XPALPHAS && ch == ' ') { newchars[ni++] = '+'; /* convert spaces to pluses */ } else { newchars[ni++] = '%'; newchars[ni++] = digits[ch >> 4]; newchars[ni++] = digits[ch & 0xF]; } } else { newchars[ni++] = '%'; newchars[ni++] = 'u'; newchars[ni++] = digits[ch >> 12]; newchars[ni++] = digits[(ch & 0xF00) >> 8]; newchars[ni++] = digits[(ch & 0xF0) >> 4]; newchars[ni++] = digits[ch & 0xF]; } } JS_ASSERT(ni == newlength); newchars[newlength] = 0; str = js_NewString(cx, newchars, newlength, 0); if (!str) { JS_free(cx, newchars); return JS_FALSE; } *rval = STRING_TO_JSVAL(str); return JS_TRUE; } #undef IS_OK /* See ECMA-262 15.1.2.5 */ static JSBool str_unescape(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) { JSString *str; size_t i, ni, length; const jschar *chars; jschar *newchars; jschar ch; str = js_ValueToString(cx, argv[0]); if (!str) return JS_FALSE; argv[0] = STRING_TO_JSVAL(str); chars = JSSTRING_CHARS(str); length = JSSTRING_LENGTH(str); /* Don't bother allocating less space for the new string. */ newchars = (jschar *) JS_malloc(cx, (length + 1) * sizeof(jschar)); if (!newchars) return JS_FALSE; ni = i = 0; while (i < length) { ch = chars[i++]; if (ch == '%') { if (i + 1 < length && JS7_ISHEX(chars[i]) && JS7_ISHEX(chars[i + 1])) { ch = JS7_UNHEX(chars[i]) * 16 + JS7_UNHEX(chars[i + 1]); i += 2; } else if (i + 4 < length && chars[i] == 'u' && JS7_ISHEX(chars[i + 1]) && JS7_ISHEX(chars[i + 2]) && JS7_ISHEX(chars[i + 3]) && JS7_ISHEX(chars[i + 4])) { ch = (((((JS7_UNHEX(chars[i + 1]) << 4) + JS7_UNHEX(chars[i + 2])) << 4) + JS7_UNHEX(chars[i + 3])) << 4) + JS7_UNHEX(chars[i + 4]); i += 5; } } newchars[ni++] = ch; } newchars[ni] = 0; str = js_NewString(cx, newchars, ni, 0); if (!str) { JS_free(cx, newchars); return JS_FALSE; } *rval = STRING_TO_JSVAL(str); return JS_TRUE; } #if JS_HAS_UNEVAL static JSBool str_uneval(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) { JSString *str; str = js_ValueToSource(cx, argv[0]); if (!str) return JS_FALSE; *rval = STRING_TO_JSVAL(str); return JS_TRUE; } #endif const char js_escape_str[] = "escape"; const char js_unescape_str[] = "unescape"; #if JS_HAS_UNEVAL const char js_uneval_str[] = "uneval"; #endif const char js_decodeURI_str[] = "decodeURI"; const char js_encodeURI_str[] = "encodeURI"; const char js_decodeURIComponent_str[] = "decodeURIComponent"; const char js_encodeURIComponent_str[] = "encodeURIComponent"; static JSFunctionSpec string_functions[] = { {js_escape_str, js_str_escape, 1,0,0}, {js_unescape_str, str_unescape, 1,0,0}, #if JS_HAS_UNEVAL {js_uneval_str, str_uneval, 1,0,0}, #endif {js_decodeURI_str, str_decodeURI, 1,0,0}, {js_encodeURI_str, str_encodeURI, 1,0,0}, {js_decodeURIComponent_str, str_decodeURI_Component, 1,0,0}, {js_encodeURIComponent_str, str_encodeURI_Component, 1,0,0}, {0,0,0,0,0} }; jschar js_empty_ucstr[] = {0}; JSSubString js_EmptySubString = {0, js_empty_ucstr}; enum string_tinyid { STRING_LENGTH = -1 }; static JSPropertySpec string_props[] = { {js_length_str, STRING_LENGTH, JSPROP_READONLY|JSPROP_PERMANENT|JSPROP_SHARED, 0,0}, {0,0,0,0,0} }; static JSBool str_getProperty(JSContext *cx, JSObject *obj, jsval id, jsval *vp) { jsval v; JSString *str; jsint slot; if (!JSVAL_IS_INT(id)) return JS_TRUE; slot = JSVAL_TO_INT(id); if (slot == STRING_LENGTH) { if (OBJ_GET_CLASS(cx, obj) == &js_StringClass) { /* Follow ECMA-262 by fetching intrinsic length of our string. */ v = OBJ_GET_SLOT(cx, obj, JSSLOT_PRIVATE); JS_ASSERT(JSVAL_IS_STRING(v)); str = JSVAL_TO_STRING(v); } else { /* Preserve compatibility: convert obj to a string primitive. */ str = js_ValueToString(cx, OBJECT_TO_JSVAL(obj)); if (!str) return JS_FALSE; } *vp = INT_TO_JSVAL((jsint) JSSTRING_LENGTH(str)); } return JS_TRUE; } #define STRING_ELEMENT_ATTRS (JSPROP_ENUMERATE|JSPROP_READONLY|JSPROP_PERMANENT) static JSBool str_enumerate(JSContext *cx, JSObject *obj) { jsval v; JSString *str, *str1; size_t i, length; v = OBJ_GET_SLOT(cx, obj, JSSLOT_PRIVATE); JS_ASSERT(JSVAL_IS_STRING(v)); str = JSVAL_TO_STRING(v); length = JSSTRING_LENGTH(str); for (i = 0; i < length; i++) { str1 = js_NewDependentString(cx, str, i, 1, 0); if (!str1) return JS_FALSE; if (!OBJ_DEFINE_PROPERTY(cx, obj, INT_TO_JSID(i), STRING_TO_JSVAL(str1), NULL, NULL, STRING_ELEMENT_ATTRS, NULL)) { return JS_FALSE; } } return JS_TRUE; } static JSBool str_resolve(JSContext *cx, JSObject *obj, jsval id, uintN flags, JSObject **objp) { jsval v; JSString *str, *str1; jsint slot; if (!JSVAL_IS_INT(id) || (flags & JSRESOLVE_ASSIGNING)) return JS_TRUE; v = OBJ_GET_SLOT(cx, obj, JSSLOT_PRIVATE); JS_ASSERT(JSVAL_IS_STRING(v)); str = JSVAL_TO_STRING(v); slot = JSVAL_TO_INT(id); if ((size_t)slot < JSSTRING_LENGTH(str)) { str1 = js_NewDependentString(cx, str, (size_t)slot, 1, 0); if (!str1) return JS_FALSE; if (!OBJ_DEFINE_PROPERTY(cx, obj, INT_TO_JSID(slot), STRING_TO_JSVAL(str1), NULL, NULL, STRING_ELEMENT_ATTRS, NULL)) { return JS_FALSE; } *objp = obj; } return JS_TRUE; } JSClass js_StringClass = { js_String_str, JSCLASS_HAS_PRIVATE | JSCLASS_NEW_RESOLVE | JSCLASS_HAS_CACHED_PROTO(JSProto_String), JS_PropertyStub, JS_PropertyStub, str_getProperty, JS_PropertyStub, str_enumerate, (JSResolveOp)str_resolve, JS_ConvertStub, JS_FinalizeStub, JSCLASS_NO_OPTIONAL_MEMBERS }; #if JS_HAS_TOSOURCE /* * String.prototype.quote is generic (as are most string methods), unlike * toSource, toString, and valueOf. */ static JSBool str_quote(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) { JSString *str; str = js_ValueToString(cx, OBJECT_TO_JSVAL(obj)); if (!str) return JS_FALSE; argv[-1] = STRING_TO_JSVAL(str); str = js_QuoteString(cx, str, '"'); if (!str) return JS_FALSE; *rval = STRING_TO_JSVAL(str); return JS_TRUE; } static JSBool str_toSource(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) { jsval v; JSString *str; size_t i, j, k, n; char buf[16]; jschar *s, *t; if (JSVAL_IS_STRING((jsval)obj)) { v = (jsval)obj; } else { if (!JS_InstanceOf(cx, obj, &js_StringClass, argv)) return JS_FALSE; v = OBJ_GET_SLOT(cx, obj, JSSLOT_PRIVATE); if (!JSVAL_IS_STRING(v)) return js_obj_toSource(cx, obj, argc, argv, rval); } str = js_QuoteString(cx, JSVAL_TO_STRING(v), '"'); if (!str) return JS_FALSE; j = JS_snprintf(buf, sizeof buf, "(new %s(", js_StringClass.name); s = JSSTRING_CHARS(str); k = JSSTRING_LENGTH(str); n = j + k + 2; t = (jschar *) JS_malloc(cx, (n + 1) * sizeof(jschar)); if (!t) return JS_FALSE; for (i = 0; i < j; i++) t[i] = buf[i]; for (j = 0; j < k; i++, j++) t[i] = s[j]; t[i++] = ')'; t[i++] = ')'; t[i] = 0; str = js_NewString(cx, t, n, 0); if (!str) { JS_free(cx, t); return JS_FALSE; } *rval = STRING_TO_JSVAL(str); return JS_TRUE; } #endif /* JS_HAS_TOSOURCE */ static JSBool str_toString(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) { jsval v; if (JSVAL_IS_STRING((jsval)obj)) { *rval = (jsval)obj; return JS_TRUE; } if (!JS_InstanceOf(cx, obj, &js_StringClass, argv)) return JS_FALSE; v = OBJ_GET_SLOT(cx, obj, JSSLOT_PRIVATE); if (!JSVAL_IS_STRING(v)) return js_obj_toString(cx, obj, argc, argv, rval); *rval = v; return JS_TRUE; } static JSBool str_valueOf(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) { if (JSVAL_IS_STRING((jsval)obj)) { *rval = (jsval)obj; return JS_TRUE; } if (!JS_InstanceOf(cx, obj, &js_StringClass, argv)) return JS_FALSE; *rval = OBJ_GET_SLOT(cx, obj, JSSLOT_PRIVATE); return JS_TRUE; } /* * Java-like string native methods. */ static JSBool str_substring(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) { JSString *str; jsdouble d; jsdouble length, begin, end; str = js_ValueToString(cx, OBJECT_TO_JSVAL(obj)); if (!str) return JS_FALSE; argv[-1] = STRING_TO_JSVAL(str); if (argc != 0) { if (!js_ValueToNumber(cx, argv[0], &d)) return JS_FALSE; length = JSSTRING_LENGTH(str); begin = js_DoubleToInteger(d); if (begin < 0) begin = 0; else if (begin > length) begin = length; if (argc == 1) { end = length; } else { if (!js_ValueToNumber(cx, argv[1], &d)) return JS_FALSE; end = js_DoubleToInteger(d); if (end < 0) end = 0; else if (end > length) end = length; if (end < begin) { /* ECMA emulates old JDK1.0 java.lang.String.substring. */ jsdouble tmp = begin; begin = end; end = tmp; } } str = js_NewDependentString(cx, str, (size_t)begin, (size_t)(end - begin), 0); if (!str) return JS_FALSE; } *rval = STRING_TO_JSVAL(str); return JS_TRUE; } static JSBool str_toLowerCase(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) { JSString *str; size_t i, n; jschar *s, *news; str = js_ValueToString(cx, OBJECT_TO_JSVAL(obj)); if (!str) return JS_FALSE; argv[-1] = STRING_TO_JSVAL(str); n = JSSTRING_LENGTH(str); news = (jschar *) JS_malloc(cx, (n + 1) * sizeof(jschar)); if (!news) return JS_FALSE; s = JSSTRING_CHARS(str); for (i = 0; i < n; i++) news[i] = JS_TOLOWER(s[i]); news[n] = 0; str = js_NewString(cx, news, n, 0); if (!str) { JS_free(cx, news); return JS_FALSE; } *rval = STRING_TO_JSVAL(str); return JS_TRUE; } static JSBool str_toLocaleLowerCase(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) { JSString *str; /* * Forcefully ignore the first (or any) argument and return toLowerCase(), * ECMA has reserved that argument, presumably for defining the locale. */ if (cx->localeCallbacks && cx->localeCallbacks->localeToLowerCase) { str = js_ValueToString(cx, OBJECT_TO_JSVAL(obj)); if (!str) return JS_FALSE; argv[-1] = STRING_TO_JSVAL(str); return cx->localeCallbacks->localeToLowerCase(cx, str, rval); } return str_toLowerCase(cx, obj, 0, argv, rval); } static JSBool str_toUpperCase(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) { JSString *str; size_t i, n; jschar *s, *news; str = js_ValueToString(cx, OBJECT_TO_JSVAL(obj)); if (!str) return JS_FALSE; argv[-1] = STRING_TO_JSVAL(str); n = JSSTRING_LENGTH(str); news = (jschar *) JS_malloc(cx, (n + 1) * sizeof(jschar)); if (!news) return JS_FALSE; s = JSSTRING_CHARS(str); for (i = 0; i < n; i++) news[i] = JS_TOUPPER(s[i]); news[n] = 0; str = js_NewString(cx, news, n, 0); if (!str) { JS_free(cx, news); return JS_FALSE; } *rval = STRING_TO_JSVAL(str); return JS_TRUE; } static JSBool str_toLocaleUpperCase(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) { JSString *str; /* * Forcefully ignore the first (or any) argument and return toUpperCase(), * ECMA has reserved that argument, presumbaly for defining the locale. */ if (cx->localeCallbacks && cx->localeCallbacks->localeToUpperCase) { str = js_ValueToString(cx, OBJECT_TO_JSVAL(obj)); if (!str) return JS_FALSE; argv[-1] = STRING_TO_JSVAL(str); return cx->localeCallbacks->localeToUpperCase(cx, str, rval); } return str_toUpperCase(cx, obj, 0, argv, rval); } static JSBool str_localeCompare(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) { JSString *str, *thatStr; str = js_ValueToString(cx, OBJECT_TO_JSVAL(obj)); if (!str) return JS_FALSE; argv[-1] = STRING_TO_JSVAL(str); if (argc == 0) { *rval = JSVAL_ZERO; } else { thatStr = js_ValueToString(cx, argv[0]); if (!thatStr) return JS_FALSE; if (cx->localeCallbacks && cx->localeCallbacks->localeCompare) { argv[0] = STRING_TO_JSVAL(thatStr); return cx->localeCallbacks->localeCompare(cx, str, thatStr, rval); } *rval = INT_TO_JSVAL(js_CompareStrings(str, thatStr)); } return JS_TRUE; } static JSBool str_charAt(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) { JSString *str; jsdouble d; size_t index; str = js_ValueToString(cx, OBJECT_TO_JSVAL(obj)); if (!str) return JS_FALSE; argv[-1] = STRING_TO_JSVAL(str); if (argc == 0) { d = 0.0; } else { if (!js_ValueToNumber(cx, argv[0], &d)) return JS_FALSE; d = js_DoubleToInteger(d); } if (d < 0 || JSSTRING_LENGTH(str) <= d) { *rval = JS_GetEmptyStringValue(cx); } else { index = (size_t)d; str = js_NewDependentString(cx, str, index, 1, 0); if (!str) return JS_FALSE; *rval = STRING_TO_JSVAL(str); } return JS_TRUE; } static JSBool str_charCodeAt(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) { JSString *str; jsdouble d; size_t index; str = js_ValueToString(cx, OBJECT_TO_JSVAL(obj)); if (!str) return JS_FALSE; argv[-1] = STRING_TO_JSVAL(str); if (argc == 0) { d = 0.0; } else { if (!js_ValueToNumber(cx, argv[0], &d)) return JS_FALSE; d = js_DoubleToInteger(d); } if (d < 0 || JSSTRING_LENGTH(str) <= d) { *rval = JS_GetNaNValue(cx); } else { index = (size_t)d; *rval = INT_TO_JSVAL((jsint) JSSTRING_CHARS(str)[index]); } return JS_TRUE; } jsint js_BoyerMooreHorspool(const jschar *text, jsint textlen, const jschar *pat, jsint patlen, jsint start) { jsint i, j, k, m; uint8 skip[BMH_CHARSET_SIZE]; jschar c; JS_ASSERT(0 < patlen && patlen <= BMH_PATLEN_MAX); for (i = 0; i < BMH_CHARSET_SIZE; i++) skip[i] = (uint8)patlen; m = patlen - 1; for (i = 0; i < m; i++) { c = pat[i]; if (c >= BMH_CHARSET_SIZE) return BMH_BAD_PATTERN; skip[c] = (uint8)(m - i); } for (k = start + m; k < textlen; k += ((c = text[k]) >= BMH_CHARSET_SIZE) ? patlen : skip[c]) { for (i = k, j = m; ; i--, j--) { if (j < 0) return i + 1; if (text[i] != pat[j]) break; } } return -1; } static JSBool str_indexOf(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) { JSString *str, *str2; jsint i, j, index, textlen, patlen; const jschar *text, *pat; jsdouble d; str = js_ValueToString(cx, OBJECT_TO_JSVAL(obj)); if (!str) return JS_FALSE; argv[-1] = STRING_TO_JSVAL(str); text = JSSTRING_CHARS(str); textlen = (jsint) JSSTRING_LENGTH(str); str2 = js_ValueToString(cx, argv[0]); if (!str2) return JS_FALSE; argv[0] = STRING_TO_JSVAL(str2); pat = JSSTRING_CHARS(str2); patlen = (jsint) JSSTRING_LENGTH(str2); if (argc > 1) { if (!js_ValueToNumber(cx, argv[1], &d)) return JS_FALSE; d = js_DoubleToInteger(d); if (d < 0) i = 0; else if (d > textlen) i = textlen; else i = (jsint)d; } else { i = 0; } if (patlen == 0) { *rval = INT_TO_JSVAL(i); return JS_TRUE; } /* XXX tune the BMH threshold (512) */ if ((jsuint)(patlen - 2) <= BMH_PATLEN_MAX - 2 && textlen >= 512) { index = js_BoyerMooreHorspool(text, textlen, pat, patlen, i); if (index != BMH_BAD_PATTERN) goto out; } index = -1; j = 0; while (i + j < textlen) { if (text[i + j] == pat[j]) { if (++j == patlen) { index = i; break; } } else { i++; j = 0; } } out: *rval = INT_TO_JSVAL(index); return JS_TRUE; } static JSBool str_lastIndexOf(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) { JSString *str, *str2; const jschar *text, *pat; jsint i, j, textlen, patlen; jsdouble d; str = js_ValueToString(cx, OBJECT_TO_JSVAL(obj)); if (!str) return JS_FALSE; argv[-1] = STRING_TO_JSVAL(str); text = JSSTRING_CHARS(str); textlen = (jsint) JSSTRING_LENGTH(str); str2 = js_ValueToString(cx, argv[0]); if (!str2) return JS_FALSE; argv[0] = STRING_TO_JSVAL(str2); pat = JSSTRING_CHARS(str2); patlen = (jsint) JSSTRING_LENGTH(str2); if (argc > 1) { if (!js_ValueToNumber(cx, argv[1], &d)) return JS_FALSE; if (JSDOUBLE_IS_NaN(d)) { i = textlen; } else { d = js_DoubleToInteger(d); if (d < 0) i = 0; else if (d > textlen) i = textlen; else i = (jsint)d; } } else { i = textlen; } if (patlen == 0) { *rval = INT_TO_JSVAL(i); return JS_TRUE; } j = 0; while (i >= 0) { /* Don't assume that text is NUL-terminated: it could be dependent. */ if (i + j < textlen && text[i + j] == pat[j]) { if (++j == patlen) break; } else { i--; j = 0; } } *rval = INT_TO_JSVAL(i); return JS_TRUE; } /* * Perl-inspired string functions. */ typedef struct GlobData { uintN flags; /* inout: mode and flag bits, see below */ uintN optarg; /* in: index of optional flags argument */ JSString *str; /* out: 'this' parameter object as string */ JSRegExp *regexp; /* out: regexp parameter object private data */ } GlobData; /* * Mode and flag bit definitions for match_or_replace's GlobData.flags field. */ #define MODE_MATCH 0x00 /* in: return match array on success */ #define MODE_REPLACE 0x01 /* in: match and replace */ #define MODE_SEARCH 0x02 /* in: search only, return match index or -1 */ #define GET_MODE(f) ((f) & 0x03) #define FORCE_FLAT 0x04 /* in: force flat (non-regexp) string match */ #define KEEP_REGEXP 0x08 /* inout: keep GlobData.regexp alive for caller of match_or_replace; if set on input but clear on output, regexp ownership does not pass to caller */ #define GLOBAL_REGEXP 0x10 /* out: regexp had the 'g' flag */ static JSBool match_or_replace(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, JSBool (*glob)(JSContext *cx, jsint count, GlobData *data), GlobData *data, jsval *rval) { JSString *str, *src, *opt; JSObject *reobj; JSRegExp *re; size_t index, length; JSBool ok, test; jsint count; str = js_ValueToString(cx, OBJECT_TO_JSVAL(obj)); if (!str) return JS_FALSE; argv[-1] = STRING_TO_JSVAL(str); data->str = str; if (JSVAL_IS_REGEXP(cx, argv[0])) { reobj = JSVAL_TO_OBJECT(argv[0]); re = (JSRegExp *) JS_GetPrivate(cx, reobj); } else { src = js_ValueToString(cx, argv[0]); if (!src) return JS_FALSE; if (data->optarg < argc) { argv[0] = STRING_TO_JSVAL(src); opt = js_ValueToString(cx, argv[data->optarg]); if (!opt) return JS_FALSE; } else { opt = NULL; } re = js_NewRegExpOpt(cx, NULL, src, opt, (data->flags & FORCE_FLAT) != 0); if (!re) return JS_FALSE; reobj = NULL; } /* From here on, all control flow must reach the matching DROP. */ data->regexp = re; HOLD_REGEXP(cx, re); if (re->flags & JSREG_GLOB) data->flags |= GLOBAL_REGEXP; index = 0; if (GET_MODE(data->flags) == MODE_SEARCH) { ok = js_ExecuteRegExp(cx, re, str, &index, JS_TRUE, rval); if (ok) { *rval = (*rval == JSVAL_TRUE) ? INT_TO_JSVAL(cx->regExpStatics.leftContext.length) : INT_TO_JSVAL(-1); } } else if (data->flags & GLOBAL_REGEXP) { if (reobj) { /* Set the lastIndex property's reserved slot to 0. */ ok = js_SetLastIndex(cx, reobj, 0); } else { ok = JS_TRUE; } if (ok) { length = JSSTRING_LENGTH(str); for (count = 0; index <= length; count++) { ok = js_ExecuteRegExp(cx, re, str, &index, JS_TRUE, rval); if (!ok || *rval != JSVAL_TRUE) break; ok = glob(cx, count, data); if (!ok) break; if (cx->regExpStatics.lastMatch.length == 0) { if (index == length) break; index++; } } } } else { if (GET_MODE(data->flags) == MODE_REPLACE) { test = JS_TRUE; } else { /* * MODE_MATCH implies str_match is being called from a script or a * scripted function. If the caller cares only about testing null * vs. non-null return value, optimize away the array object that * would normally be returned in *rval. */ JSStackFrame *fp = cx->fp->down; /* Skip Function.prototype.call and .apply frames. */ while (fp && !fp->pc) { JS_ASSERT(!fp->script); fp = fp->down; } /* Assume a full array result is required, then prove otherwise. */ test = JS_FALSE; if (fp) { JS_ASSERT(*fp->pc == JSOP_CALL || *fp->pc == JSOP_NEW); JS_ASSERT(js_CodeSpec[*fp->pc].length == 3); switch (fp->pc[3]) { case JSOP_POP: case JSOP_IFEQ: case JSOP_IFNE: case JSOP_IFEQX: case JSOP_IFNEX: test = JS_TRUE; break; default:; } } } ok = js_ExecuteRegExp(cx, re, str, &index, test, rval); } DROP_REGEXP(cx, re); if (reobj) { /* Tell our caller that it doesn't need to destroy data->regexp. */ data->flags &= ~KEEP_REGEXP; } else if (!(data->flags & KEEP_REGEXP)) { /* Caller didn't want to keep data->regexp, so null and destroy it. */ data->regexp = NULL; js_DestroyRegExp(cx, re); } return ok; } typedef struct MatchData { GlobData base; jsval *arrayval; /* NB: local root pointer */ } MatchData; static JSBool match_glob(JSContext *cx, jsint count, GlobData *data) { MatchData *mdata; JSObject *arrayobj; JSSubString *matchsub; JSString *matchstr; jsval v; mdata = (MatchData *)data; arrayobj = JSVAL_TO_OBJECT(*mdata->arrayval); if (!arrayobj) { arrayobj = js_ConstructObject(cx, &js_ArrayClass, NULL, NULL, 0, NULL); if (!arrayobj) return JS_FALSE; *mdata->arrayval = OBJECT_TO_JSVAL(arrayobj); } matchsub = &cx->regExpStatics.lastMatch; matchstr = js_NewStringCopyN(cx, matchsub->chars, matchsub->length, 0); if (!matchstr) return JS_FALSE; v = STRING_TO_JSVAL(matchstr); return js_SetProperty(cx, arrayobj, INT_TO_JSID(count), &v); } static JSBool str_match(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) { MatchData mdata; JSBool ok; mdata.base.flags = MODE_MATCH; mdata.base.optarg = 1; mdata.arrayval = &argv[2]; *mdata.arrayval = JSVAL_NULL; ok = match_or_replace(cx, obj, argc, argv, match_glob, &mdata.base, rval); if (ok && !JSVAL_IS_NULL(*mdata.arrayval)) *rval = *mdata.arrayval; return ok; } static JSBool str_search(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) { GlobData data; data.flags = MODE_SEARCH; data.optarg = 1; return match_or_replace(cx, obj, argc, argv, NULL, &data, rval); } typedef struct ReplaceData { GlobData base; /* base struct state */ JSObject *lambda; /* replacement function object or null */ JSString *repstr; /* replacement string */ jschar *dollar; /* null or pointer to first $ in repstr */ jschar *dollarEnd; /* limit pointer for js_strchr_limit */ jschar *chars; /* result chars, null initially */ size_t length; /* result length, 0 initially */ jsint index; /* index in result of next replacement */ jsint leftIndex; /* left context index in base.str->chars */ JSSubString dollarStr; /* for "$$" interpret_dollar result */ } ReplaceData; static JSSubString * interpret_dollar(JSContext *cx, jschar *dp, jschar *ep, ReplaceData *rdata, size_t *skip) { JSRegExpStatics *res; jschar dc, *cp; uintN num, tmp; JS_ASSERT(*dp == '$'); /* If there is only a dollar, bail now */ if (dp + 1 >= ep) return NULL; /* Interpret all Perl match-induced dollar variables. */ res = &cx->regExpStatics; dc = dp[1]; if (JS7_ISDEC(dc)) { /* ECMA-262 Edition 3: 1-9 or 01-99 */ num = JS7_UNDEC(dc); if (num > res->parenCount) return NULL; cp = dp + 2; if (cp < ep && (dc = *cp, JS7_ISDEC(dc))) { tmp = 10 * num + JS7_UNDEC(dc); if (tmp <= res->parenCount) { cp++; num = tmp; } } if (num == 0) return NULL; /* Adjust num from 1 $n-origin to 0 array-index-origin. */ num--; *skip = cp - dp; return REGEXP_PAREN_SUBSTRING(res, num); } *skip = 2; switch (dc) { case '$': rdata->dollarStr.chars = dp; rdata->dollarStr.length = 1; return &rdata->dollarStr; case '&': return &res->lastMatch; case '+': return &res->lastParen; case '`': return &res->leftContext; case '\'': return &res->rightContext; } return NULL; } static JSBool find_replen(JSContext *cx, ReplaceData *rdata, size_t *sizep) { JSString *repstr; size_t replen, skip; jschar *dp, *ep; JSSubString *sub; JSObject *lambda; lambda = rdata->lambda; if (lambda) { uintN argc, i, j, m, n, p; jsval *sp, *oldsp, rval; void *mark; JSStackFrame *fp; JSBool ok; /* * Save the regExpStatics from the current regexp, since they may be * clobbered by a RegExp usage in the lambda function. Note that all * members of JSRegExpStatics are JSSubStrings, so not GC roots, save * input, which is rooted otherwise via argv[-1] in str_replace. */ JSRegExpStatics save = cx->regExpStatics; JSBool freeMoreParens = JS_FALSE; /* * In the lambda case, not only do we find the replacement string's * length, we compute repstr and return it via rdata for use within * do_replace. The lambda is called with arguments ($&, $1, $2, ..., * index, input), i.e., all the properties of a regexp match array. * For $&, etc., we must create string jsvals from cx->regExpStatics. * We grab up stack space to keep the newborn strings GC-rooted. */ p = rdata->base.regexp->parenCount; argc = 1 + p + 2; sp = js_AllocStack(cx, 2 + argc, &mark); if (!sp) return JS_FALSE; /* Push lambda and its 'this' parameter. */ *sp++ = OBJECT_TO_JSVAL(lambda); *sp++ = OBJECT_TO_JSVAL(OBJ_GET_PARENT(cx, lambda)); #define PUSH_REGEXP_STATIC(sub) \ JS_BEGIN_MACRO \ JSString *str = js_NewStringCopyN(cx, \ cx->regExpStatics.sub.chars, \ cx->regExpStatics.sub.length, \ 0); \ if (!str) { \ ok = JS_FALSE; \ goto lambda_out; \ } \ *sp++ = STRING_TO_JSVAL(str); \ JS_END_MACRO /* Push $&, $1, $2, ... */ PUSH_REGEXP_STATIC(lastMatch); i = 0; m = cx->regExpStatics.parenCount; n = JS_MIN(m, 9); for (j = 0; i < n; i++, j++) PUSH_REGEXP_STATIC(parens[j]); for (j = 0; i < m; i++, j++) PUSH_REGEXP_STATIC(moreParens[j]); /* * We need to clear moreParens in the top-of-stack cx->regExpStatics * to it won't be possibly realloc'ed, leaving the bottom-of-stack * moreParens pointing to freed memory. */ cx->regExpStatics.moreParens = NULL; freeMoreParens = JS_TRUE; #undef PUSH_REGEXP_STATIC /* Make sure to push undefined for any unmatched parens. */ for (; i < p; i++) *sp++ = JSVAL_VOID; /* Push match index and input string. */ *sp++ = INT_TO_JSVAL((jsint)cx->regExpStatics.leftContext.length); *sp++ = STRING_TO_JSVAL(rdata->base.str); /* Lift current frame to include the args and do the call. */ fp = cx->fp; oldsp = fp->sp; fp->sp = sp; ok = js_Invoke(cx, argc, JSINVOKE_INTERNAL); rval = fp->sp[-1]; fp->sp = oldsp; if (ok) { /* * NB: we count on the newborn string root to hold any string * created by this js_ValueToString that would otherwise be GC- * able, until we use rdata->repstr in do_replace. */ repstr = js_ValueToString(cx, rval); if (!repstr) { ok = JS_FALSE; } else { rdata->repstr = repstr; *sizep = JSSTRING_LENGTH(repstr); } } lambda_out: js_FreeStack(cx, mark); if (freeMoreParens) JS_free(cx, cx->regExpStatics.moreParens); cx->regExpStatics = save; return ok; } repstr = rdata->repstr; replen = JSSTRING_LENGTH(repstr); for (dp = rdata->dollar, ep = rdata->dollarEnd; dp; dp = js_strchr_limit(dp, '$', ep)) { sub = interpret_dollar(cx, dp, ep, rdata, &skip); if (sub) { replen += sub->length - skip; dp += skip; } else dp++; } *sizep = replen; return JS_TRUE; } static void do_replace(JSContext *cx, ReplaceData *rdata, jschar *chars) { JSString *repstr; jschar *bp, *cp, *dp, *ep; size_t len, skip; JSSubString *sub; repstr = rdata->repstr; bp = cp = JSSTRING_CHARS(repstr); for (dp = rdata->dollar, ep = rdata->dollarEnd; dp; dp = js_strchr_limit(dp, '$', ep)) { len = dp - cp; js_strncpy(chars, cp, len); chars += len; cp = dp; sub = interpret_dollar(cx, dp, ep, rdata, &skip); if (sub) { len = sub->length; js_strncpy(chars, sub->chars, len); chars += len; cp += skip; dp += skip; } else { dp++; } } js_strncpy(chars, cp, JSSTRING_LENGTH(repstr) - (cp - bp)); } static JSBool replace_glob(JSContext *cx, jsint count, GlobData *data) { ReplaceData *rdata; JSString *str; size_t leftoff, leftlen, replen, growth; const jschar *left; jschar *chars; rdata = (ReplaceData *)data; str = data->str; leftoff = rdata->leftIndex; left = JSSTRING_CHARS(str) + leftoff; leftlen = cx->regExpStatics.lastMatch.chars - left; rdata->leftIndex = cx->regExpStatics.lastMatch.chars - JSSTRING_CHARS(str); rdata->leftIndex += cx->regExpStatics.lastMatch.length; if (!find_replen(cx, rdata, &replen)) return JS_FALSE; growth = leftlen + replen; chars = (jschar *) (rdata->chars ? JS_realloc(cx, rdata->chars, (rdata->length + growth + 1) * sizeof(jschar)) : JS_malloc(cx, (growth + 1) * sizeof(jschar))); if (!chars) { JS_free(cx, rdata->chars); rdata->chars = NULL; return JS_FALSE; } rdata->chars = chars; rdata->length += growth; chars += rdata->index; rdata->index += growth; js_strncpy(chars, left, leftlen); chars += leftlen; do_replace(cx, rdata, chars); return JS_TRUE; } static JSBool str_replace(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) { JSObject *lambda; JSString *repstr, *str; ReplaceData rdata; JSBool ok; jschar *chars; size_t leftlen, rightlen, length; if (JS_TypeOfValue(cx, argv[1]) == JSTYPE_FUNCTION) { lambda = JSVAL_TO_OBJECT(argv[1]); repstr = NULL; } else { if (!JS_ConvertValue(cx, argv[1], JSTYPE_STRING, &argv[1])) return JS_FALSE; repstr = JSVAL_TO_STRING(argv[1]); lambda = NULL; } /* * For ECMA Edition 3, the first argument is to be converted to a string * to match in a "flat" sense (without regular expression metachars having * special meanings) UNLESS the first arg is a RegExp object. */ rdata.base.flags = MODE_REPLACE | KEEP_REGEXP | FORCE_FLAT; rdata.base.optarg = 2; rdata.lambda = lambda; rdata.repstr = repstr; if (repstr) { rdata.dollarEnd = JSSTRING_CHARS(repstr) + JSSTRING_LENGTH(repstr); rdata.dollar = js_strchr_limit(JSSTRING_CHARS(repstr), '$', rdata.dollarEnd); } else { rdata.dollar = rdata.dollarEnd = NULL; } rdata.chars = NULL; rdata.length = 0; rdata.index = 0; rdata.leftIndex = 0; ok = match_or_replace(cx, obj, argc, argv, replace_glob, &rdata.base, rval); if (!ok) return JS_FALSE; if (!rdata.chars) { if ((rdata.base.flags & GLOBAL_REGEXP) || *rval != JSVAL_TRUE) { /* Didn't match even once. */ *rval = STRING_TO_JSVAL(rdata.base.str); goto out; } leftlen = cx->regExpStatics.leftContext.length; ok = find_replen(cx, &rdata, &length); if (!ok) goto out; length += leftlen; chars = (jschar *) JS_malloc(cx, (length + 1) * sizeof(jschar)); if (!chars) { ok = JS_FALSE; goto out; } js_strncpy(chars, cx->regExpStatics.leftContext.chars, leftlen); do_replace(cx, &rdata, chars + leftlen); rdata.chars = chars; rdata.length = length; } rightlen = cx->regExpStatics.rightContext.length; length = rdata.length + rightlen; chars = (jschar *) JS_realloc(cx, rdata.chars, (length + 1) * sizeof(jschar)); if (!chars) { JS_free(cx, rdata.chars); ok = JS_FALSE; goto out; } js_strncpy(chars + rdata.length, cx->regExpStatics.rightContext.chars, rightlen); chars[length] = 0; str = js_NewString(cx, chars, length, 0); if (!str) { JS_free(cx, chars); ok = JS_FALSE; goto out; } *rval = STRING_TO_JSVAL(str); out: /* If KEEP_REGEXP is still set, it's our job to destroy regexp now. */ if (rdata.base.flags & KEEP_REGEXP) js_DestroyRegExp(cx, rdata.base.regexp); return ok; } /* * Subroutine used by str_split to find the next split point in str, starting * at offset *ip and looking either for the separator substring given by sep, * or for the next re match. In the re case, return the matched separator in * *sep, and the possibly updated offset in *ip. * * Return -2 on error, -1 on end of string, >= 0 for a valid index of the next * separator occurrence if found, or str->length if no separator is found. */ static jsint find_split(JSContext *cx, JSString *str, JSRegExp *re, jsint *ip, JSSubString *sep) { jsint i, j, k; size_t length; jschar *chars; /* * Stop if past end of string. If at end of string, we will compare the * null char stored there (by js_NewString*) to sep->chars[j] in the while * loop at the end of this function, so that * * "ab,".split(',') => ["ab", ""] * * and the resulting array converts back to the string "ab," for symmetry. * However, we ape Perl and do this only if there is a sufficiently large * limit argument (see str_split). */ i = *ip; length = JSSTRING_LENGTH(str); if ((size_t)i > length) return -1; chars = JSSTRING_CHARS(str); /* * Match a regular expression against the separator at or above index i. * Call js_ExecuteRegExp with true for the test argument. On successful * match, get the separator from cx->regExpStatics.lastMatch. */ if (re) { size_t index; jsval rval; again: /* JS1.2 deviated from Perl by never matching at end of string. */ index = (size_t)i; if (!js_ExecuteRegExp(cx, re, str, &index, JS_TRUE, &rval)) return -2; if (rval != JSVAL_TRUE) { /* Mismatch: ensure our caller advances i past end of string. */ sep->length = 1; return length; } i = (jsint)index; *sep = cx->regExpStatics.lastMatch; if (sep->length == 0) { /* * Empty string match: never split on an empty match at the start * of a find_split cycle. Same rule as for an empty global match * in match_or_replace. */ if (i == *ip) { /* * "Bump-along" to avoid sticking at an empty match, but don't * bump past end of string -- our caller must do that by adding * sep->length to our return value. */ if ((size_t)i == length) return -1; i++; goto again; } if ((size_t)i == length) { /* * If there was a trivial zero-length match at the end of the * split, then we shouldn't output the matched string at the end * of the split array. See ECMA-262 Ed. 3, 15.5.4.14, Step 15. */ sep->chars = NULL; } } JS_ASSERT((size_t)i >= sep->length); return i - sep->length; } /* * Deviate from ECMA by never splitting an empty string by any separator * string into a non-empty array (an array of length 1 that contains the * empty string). */ if (!JS_VERSION_IS_ECMA(cx) && length == 0) return -1; /* * Special case: if sep is the empty string, split str into one character * substrings. Let our caller worry about whether to split once at end of * string into an empty substring. */ if (sep->length == 0) return ((size_t)i == length) ? -1 : i + 1; /* * Now that we know sep is non-empty, search starting at i in str for an * occurrence of all of sep's chars. If we find them, return the index of * the first separator char. Otherwise, return length. */ j = 0; while ((size_t)(k = i + j) < length) { if (chars[k] == sep->chars[j]) { if ((size_t)++j == sep->length) return i; } else { i++; j = 0; } } return k; } static JSBool str_split(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) { JSString *str, *sub; JSObject *arrayobj; jsval v; JSBool ok, limited; JSRegExp *re; JSSubString *sep, tmp; jsdouble d; jsint i, j; uint32 len, limit; str = js_ValueToString(cx, OBJECT_TO_JSVAL(obj)); if (!str) return JS_FALSE; argv[-1] = STRING_TO_JSVAL(str); arrayobj = js_ConstructObject(cx, &js_ArrayClass, NULL, NULL, 0, NULL); if (!arrayobj) return JS_FALSE; *rval = OBJECT_TO_JSVAL(arrayobj); if (argc == 0) { v = STRING_TO_JSVAL(str); ok = JS_SetElement(cx, arrayobj, 0, &v); } else { if (JSVAL_IS_REGEXP(cx, argv[0])) { re = (JSRegExp *) JS_GetPrivate(cx, JSVAL_TO_OBJECT(argv[0])); sep = &tmp; /* Set a magic value so we can detect a successful re match. */ sep->chars = NULL; sep->length = 0; } else { JSString *str2 = js_ValueToString(cx, argv[0]); if (!str2) return JS_FALSE; argv[0] = STRING_TO_JSVAL(str2); /* * Point sep at a local copy of str2's header because find_split * will modify sep->length. */ tmp.length = JSSTRING_LENGTH(str2); tmp.chars = JSSTRING_CHARS(str2); sep = &tmp; re = NULL; } /* Use the second argument as the split limit, if given. */ limited = (argc > 1) && !JSVAL_IS_VOID(argv[1]); limit = 0; /* Avoid warning. */ if (limited) { if (!js_ValueToNumber(cx, argv[1], &d)) return JS_FALSE; /* Clamp limit between 0 and 1 + string length. */ if (!js_DoubleToECMAUint32(cx, d, &limit)) return JS_FALSE; if (limit > JSSTRING_LENGTH(str)) limit = 1 + JSSTRING_LENGTH(str); } len = i = 0; while ((j = find_split(cx, str, re, &i, sep)) >= 0) { if (limited && len >= limit) break; sub = js_NewDependentString(cx, str, i, (size_t)(j - i), 0); if (!sub) return JS_FALSE; v = STRING_TO_JSVAL(sub); if (!JS_SetElement(cx, arrayobj, len, &v)) return JS_FALSE; len++; /* * Imitate perl's feature of including parenthesized substrings * that matched part of the delimiter in the new array, after the * split substring that was delimited. */ if (re && sep->chars) { uintN num; JSSubString *parsub; for (num = 0; num < cx->regExpStatics.parenCount; num++) { if (limited && len >= limit) break; parsub = REGEXP_PAREN_SUBSTRING(&cx->regExpStatics, num); sub = js_NewStringCopyN(cx, parsub->chars, parsub->length, 0); if (!sub) return JS_FALSE; v = STRING_TO_JSVAL(sub); if (!JS_SetElement(cx, arrayobj, len, &v)) return JS_FALSE; len++; } sep->chars = NULL; } i = j + sep->length; if (!JS_VERSION_IS_ECMA(cx)) { /* * Deviate from ECMA to imitate Perl, which omits a final * split unless a limit argument is given and big enough. */ if (!limited && (size_t)i == JSSTRING_LENGTH(str)) break; } } ok = (j != -2); } return ok; } #if JS_HAS_PERL_SUBSTR static JSBool str_substr(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) { JSString *str; jsdouble d; jsdouble length, begin, end; str = js_ValueToString(cx, OBJECT_TO_JSVAL(obj)); if (!str) return JS_FALSE; argv[-1] = STRING_TO_JSVAL(str); if (argc != 0) { if (!js_ValueToNumber(cx, argv[0], &d)) return JS_FALSE; length = JSSTRING_LENGTH(str); begin = js_DoubleToInteger(d); if (begin < 0) { begin += length; if (begin < 0) begin = 0; } else if (begin > length) { begin = length; } if (argc == 1) { end = length; } else { if (!js_ValueToNumber(cx, argv[1], &d)) return JS_FALSE; end = js_DoubleToInteger(d); if (end < 0) end = 0; end += begin; if (end > length) end = length; } str = js_NewDependentString(cx, str, (size_t)begin, (size_t)(end - begin), 0); if (!str) return JS_FALSE; } *rval = STRING_TO_JSVAL(str); return JS_TRUE; } #endif /* JS_HAS_PERL_SUBSTR */ /* * Python-esque sequence operations. */ static JSBool str_concat(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) { JSString *str, *str2; uintN i; str = js_ValueToString(cx, OBJECT_TO_JSVAL(obj)); if (!str) return JS_FALSE; argv[-1] = STRING_TO_JSVAL(str); for (i = 0; i < argc; i++) { str2 = js_ValueToString(cx, argv[i]); if (!str2) return JS_FALSE; argv[i] = STRING_TO_JSVAL(str2); str = js_ConcatStrings(cx, str, str2); if (!str) return JS_FALSE; } *rval = STRING_TO_JSVAL(str); return JS_TRUE; } static JSBool str_slice(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) { JSString *str; jsdouble d; jsdouble length, begin, end; str = js_ValueToString(cx, OBJECT_TO_JSVAL(obj)); if (!str) return JS_FALSE; argv[-1] = STRING_TO_JSVAL(str); if (argc != 0) { if (!js_ValueToNumber(cx, argv[0], &d)) return JS_FALSE; length = JSSTRING_LENGTH(str); begin = js_DoubleToInteger(d); if (begin < 0) { begin += length; if (begin < 0) begin = 0; } else if (begin > length) { begin = length; } if (argc == 1) { end = length; } else { if (!js_ValueToNumber(cx, argv[1], &d)) return JS_FALSE; end = js_DoubleToInteger(d); if (end < 0) { end += length; if (end < 0) end = 0; } else if (end > length) { end = length; } if (end < begin) end = begin; } str = js_NewDependentString(cx, str, (size_t)begin, (size_t)(end - begin), 0); if (!str) return JS_FALSE; } *rval = STRING_TO_JSVAL(str); return JS_TRUE; } #if JS_HAS_STR_HTML_HELPERS /* * HTML composition aids. */ static JSBool tagify(JSContext *cx, JSObject *obj, jsval *argv, const char *begin, JSString *param, const char *end, jsval *rval) { JSString *str; jschar *tagbuf; size_t beglen, endlen, parlen, taglen; size_t i, j; if (JSVAL_IS_STRING((jsval)obj)) { str = JSVAL_TO_STRING((jsval)obj); } else { str = js_ValueToString(cx, OBJECT_TO_JSVAL(obj)); if (!str) return JS_FALSE; argv[-1] = STRING_TO_JSVAL(str); } if (!end) end = begin; beglen = strlen(begin); taglen = 1 + beglen + 1; /* '' */ parlen = 0; /* Avoid warning. */ if (param) { parlen = JSSTRING_LENGTH(param); taglen += 2 + parlen + 1; /* '="param"' */ } endlen = strlen(end); taglen += JSSTRING_LENGTH(str) + 2 + endlen + 1; /* 'str' */ if (taglen >= ~(size_t)0 / sizeof(jschar)) { JS_ReportOutOfMemory(cx); return JS_FALSE; } tagbuf = (jschar *) JS_malloc(cx, (taglen + 1) * sizeof(jschar)); if (!tagbuf) return JS_FALSE; j = 0; tagbuf[j++] = '<'; for (i = 0; i < beglen; i++) tagbuf[j++] = (jschar)begin[i]; if (param) { tagbuf[j++] = '='; tagbuf[j++] = '"'; js_strncpy(&tagbuf[j], JSSTRING_CHARS(param), parlen); j += parlen; tagbuf[j++] = '"'; } tagbuf[j++] = '>'; js_strncpy(&tagbuf[j], JSSTRING_CHARS(str), JSSTRING_LENGTH(str)); j += JSSTRING_LENGTH(str); tagbuf[j++] = '<'; tagbuf[j++] = '/'; for (i = 0; i < endlen; i++) tagbuf[j++] = (jschar)end[i]; tagbuf[j++] = '>'; JS_ASSERT(j == taglen); tagbuf[j] = 0; str = js_NewString(cx, tagbuf, taglen, 0); if (!str) { free((char *)tagbuf); return JS_FALSE; } *rval = STRING_TO_JSVAL(str); return JS_TRUE; } static JSBool tagify_value(JSContext *cx, JSObject *obj, jsval *argv, const char *begin, const char *end, jsval *rval) { JSString *param; param = js_ValueToString(cx, argv[0]); if (!param) return JS_FALSE; argv[0] = STRING_TO_JSVAL(param); return tagify(cx, obj, argv, begin, param, end, rval); } static JSBool str_bold(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) { return tagify(cx, obj, argv, "b", NULL, NULL, rval); } static JSBool str_italics(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) { return tagify(cx, obj, argv, "i", NULL, NULL, rval); } static JSBool str_fixed(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) { return tagify(cx, obj, argv, "tt", NULL, NULL, rval); } static JSBool str_fontsize(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) { return tagify_value(cx, obj, argv, "font size", "font", rval); } static JSBool str_fontcolor(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) { return tagify_value(cx, obj, argv, "font color", "font", rval); } static JSBool str_link(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) { return tagify_value(cx, obj, argv, "a href", "a", rval); } static JSBool str_anchor(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) { return tagify_value(cx, obj, argv, "a name", "a", rval); } static JSBool str_strike(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) { return tagify(cx, obj, argv, "strike", NULL, NULL, rval); } static JSBool str_small(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) { return tagify(cx, obj, argv, "small", NULL, NULL, rval); } static JSBool str_big(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) { return tagify(cx, obj, argv, "big", NULL, NULL, rval); } static JSBool str_blink(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) { return tagify(cx, obj, argv, "blink", NULL, NULL, rval); } static JSBool str_sup(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) { return tagify(cx, obj, argv, "sup", NULL, NULL, rval); } static JSBool str_sub(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) { return tagify(cx, obj, argv, "sub", NULL, NULL, rval); } #endif /* JS_HAS_STR_HTML_HELPERS */ static JSFunctionSpec string_methods[] = { #if JS_HAS_TOSOURCE {"quote", str_quote, 0,JSFUN_GENERIC_NATIVE| JSFUN_THISP_PRIMITIVE,0}, {js_toSource_str, str_toSource, 0,JSFUN_THISP_STRING,0}, #endif /* Java-like methods. */ {js_toString_str, str_toString, 0,JSFUN_THISP_STRING,0}, {js_valueOf_str, str_valueOf, 0,JSFUN_THISP_STRING,0}, {"substring", str_substring, 2,JSFUN_GENERIC_NATIVE| JSFUN_THISP_PRIMITIVE,0}, {"toLowerCase", str_toLowerCase, 0,JSFUN_GENERIC_NATIVE| JSFUN_THISP_PRIMITIVE,0}, {"toUpperCase", str_toUpperCase, 0,JSFUN_GENERIC_NATIVE| JSFUN_THISP_PRIMITIVE,0}, {"charAt", str_charAt, 1,JSFUN_GENERIC_NATIVE| JSFUN_THISP_PRIMITIVE,0}, {"charCodeAt", str_charCodeAt, 1,JSFUN_GENERIC_NATIVE| JSFUN_THISP_PRIMITIVE,0}, {"indexOf", str_indexOf, 1,JSFUN_GENERIC_NATIVE| JSFUN_THISP_PRIMITIVE,0}, {"lastIndexOf", str_lastIndexOf, 1,JSFUN_GENERIC_NATIVE| JSFUN_THISP_PRIMITIVE,0}, {"toLocaleLowerCase", str_toLocaleLowerCase, 0,JSFUN_GENERIC_NATIVE| JSFUN_THISP_PRIMITIVE,0}, {"toLocaleUpperCase", str_toLocaleUpperCase, 0,JSFUN_GENERIC_NATIVE| JSFUN_THISP_PRIMITIVE,0}, {"localeCompare", str_localeCompare, 1,JSFUN_GENERIC_NATIVE| JSFUN_THISP_PRIMITIVE,0}, /* Perl-ish methods (search is actually Python-esque). */ {"match", str_match, 1,JSFUN_GENERIC_NATIVE| JSFUN_THISP_PRIMITIVE,2}, {"search", str_search, 1,JSFUN_GENERIC_NATIVE| JSFUN_THISP_PRIMITIVE,0}, {"replace", str_replace, 2,JSFUN_GENERIC_NATIVE| JSFUN_THISP_PRIMITIVE,0}, {"split", str_split, 2,JSFUN_GENERIC_NATIVE| JSFUN_THISP_PRIMITIVE,0}, #if JS_HAS_PERL_SUBSTR {"substr", str_substr, 2,JSFUN_GENERIC_NATIVE| JSFUN_THISP_PRIMITIVE,0}, #endif /* Python-esque sequence methods. */ {"concat", str_concat, 0,JSFUN_GENERIC_NATIVE| JSFUN_THISP_PRIMITIVE,0}, {"slice", str_slice, 0,JSFUN_GENERIC_NATIVE| JSFUN_THISP_PRIMITIVE,0}, /* HTML string methods. */ #if JS_HAS_STR_HTML_HELPERS {"bold", str_bold, 0,JSFUN_THISP_PRIMITIVE,0}, {"italics", str_italics, 0,JSFUN_THISP_PRIMITIVE,0}, {"fixed", str_fixed, 0,JSFUN_THISP_PRIMITIVE,0}, {"fontsize", str_fontsize, 1,JSFUN_THISP_PRIMITIVE,0}, {"fontcolor", str_fontcolor, 1,JSFUN_THISP_PRIMITIVE,0}, {"link", str_link, 1,JSFUN_THISP_PRIMITIVE,0}, {"anchor", str_anchor, 1,JSFUN_THISP_PRIMITIVE,0}, {"strike", str_strike, 0,JSFUN_THISP_PRIMITIVE,0}, {"small", str_small, 0,JSFUN_THISP_PRIMITIVE,0}, {"big", str_big, 0,JSFUN_THISP_PRIMITIVE,0}, {"blink", str_blink, 0,JSFUN_THISP_PRIMITIVE,0}, {"sup", str_sup, 0,JSFUN_THISP_PRIMITIVE,0}, {"sub", str_sub, 0,JSFUN_THISP_PRIMITIVE,0}, #endif {0,0,0,0,0} }; static JSBool String(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) { JSString *str; if (argc > 0) { str = js_ValueToString(cx, argv[0]); if (!str) return JS_FALSE; argv[0] = STRING_TO_JSVAL(str); } else { str = cx->runtime->emptyString; } if (!(cx->fp->flags & JSFRAME_CONSTRUCTING)) { *rval = STRING_TO_JSVAL(str); return JS_TRUE; } OBJ_SET_SLOT(cx, obj, JSSLOT_PRIVATE, STRING_TO_JSVAL(str)); return JS_TRUE; } static JSBool str_fromCharCode(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) { jschar *chars; uintN i; uint16 code; JSString *str; JS_ASSERT(argc < ARRAY_INIT_LIMIT); chars = (jschar *) JS_malloc(cx, (argc + 1) * sizeof(jschar)); if (!chars) return JS_FALSE; for (i = 0; i < argc; i++) { if (!js_ValueToUint16(cx, argv[i], &code)) { JS_free(cx, chars); return JS_FALSE; } chars[i] = (jschar)code; } chars[i] = 0; str = js_NewString(cx, chars, argc, 0); if (!str) { JS_free(cx, chars); return JS_FALSE; } *rval = STRING_TO_JSVAL(str); return JS_TRUE; } static JSFunctionSpec string_static_methods[] = { {"fromCharCode", str_fromCharCode, 1,0,0}, {0,0,0,0,0} }; JSBool js_InitRuntimeStringState(JSContext *cx) { JSRuntime *rt; JSString *empty; JSAtom *atom; rt = cx->runtime; /* Initialize string cache */ #ifdef JS_THREADSAFE JS_ASSERT(!rt->deflatedStringCacheLock); rt->deflatedStringCacheLock = JS_NEW_LOCK(); if (!rt->deflatedStringCacheLock) return JS_FALSE; #endif /* Make a permanently locked empty string. */ JS_ASSERT(!rt->emptyString); empty = js_NewStringCopyN(cx, js_empty_ucstr, 0, GCF_LOCK); if (!empty) goto bad; /* Atomize it for scripts that use '' + x to convert x to string. */ atom = js_AtomizeString(cx, empty, ATOM_PINNED); if (!atom) goto bad; rt->emptyString = empty; rt->atomState.emptyAtom = atom; return JS_TRUE; bad: #ifdef JS_THREADSAFE JS_DESTROY_LOCK(rt->deflatedStringCacheLock); rt->deflatedStringCacheLock = NULL; #endif return JS_FALSE; } void js_FinishRuntimeStringState(JSContext *cx) { JSRuntime *rt = cx->runtime; js_UnlockGCThingRT(rt, rt->emptyString); rt->emptyString = NULL; } void js_FinishDeflatedStringCache(JSRuntime *rt) { if (rt->deflatedStringCache) { JS_HashTableDestroy(rt->deflatedStringCache); rt->deflatedStringCache = NULL; } #ifdef JS_THREADSAFE if (rt->deflatedStringCacheLock) { JS_DESTROY_LOCK(rt->deflatedStringCacheLock); rt->deflatedStringCacheLock = NULL; } #endif } JSObject * js_InitStringClass(JSContext *cx, JSObject *obj) { JSObject *proto; /* Define the escape, unescape functions in the global object. */ if (!JS_DefineFunctions(cx, obj, string_functions)) return NULL; proto = JS_InitClass(cx, obj, NULL, &js_StringClass, String, 1, string_props, string_methods, NULL, string_static_methods); if (!proto) return NULL; OBJ_SET_SLOT(cx, proto, JSSLOT_PRIVATE, STRING_TO_JSVAL(cx->runtime->emptyString)); return proto; } JSString * js_NewString(JSContext *cx, jschar *chars, size_t length, uintN gcflag) { JSString *str; if (length > JSSTRING_LENGTH_MASK) { JS_ReportOutOfMemory(cx); return NULL; } str = (JSString *) js_NewGCThing(cx, gcflag | GCX_STRING, sizeof(JSString)); if (!str) return NULL; str->length = length; str->chars = chars; #ifdef DEBUG { JSRuntime *rt = cx->runtime; JS_RUNTIME_METER(rt, liveStrings); JS_RUNTIME_METER(rt, totalStrings); JS_LOCK_RUNTIME_VOID(rt, (rt->lengthSum += (double)length, rt->lengthSquaredSum += (double)length * (double)length)); } #endif return str; } JSString * js_NewDependentString(JSContext *cx, JSString *base, size_t start, size_t length, uintN gcflag) { JSDependentString *ds; if (length == 0) return cx->runtime->emptyString; if (start == 0 && length == JSSTRING_LENGTH(base)) return base; if (start > JSSTRDEP_START_MASK || (start != 0 && length > JSSTRDEP_LENGTH_MASK)) { return js_NewStringCopyN(cx, JSSTRING_CHARS(base) + start, length, gcflag); } ds = (JSDependentString *) js_NewGCThing(cx, gcflag | GCX_MUTABLE_STRING, sizeof(JSString)); if (!ds) return NULL; if (start == 0) { JSPREFIX_SET_LENGTH(ds, length); JSPREFIX_SET_BASE(ds, base); } else { JSSTRDEP_SET_START_AND_LENGTH(ds, start, length); JSSTRDEP_SET_BASE(ds, base); } #ifdef DEBUG { JSRuntime *rt = cx->runtime; JS_RUNTIME_METER(rt, liveDependentStrings); JS_RUNTIME_METER(rt, totalDependentStrings); JS_RUNTIME_METER(rt, liveStrings); JS_RUNTIME_METER(rt, totalStrings); JS_LOCK_RUNTIME_VOID(rt, (rt->strdepLengthSum += (double)length, rt->strdepLengthSquaredSum += (double)length * (double)length)); JS_LOCK_RUNTIME_VOID(rt, (rt->lengthSum += (double)length, rt->lengthSquaredSum += (double)length * (double)length)); } #endif return (JSString *)ds; } #ifdef DEBUG #include void printJSStringStats(JSRuntime *rt) { double mean = 0., var = 0., sigma = 0.; jsrefcount count = rt->totalStrings; if (count > 0 && rt->lengthSum >= 0) { mean = rt->lengthSum / count; var = count * rt->lengthSquaredSum - rt->lengthSum * rt->lengthSum; if (var < 0.0 || count <= 1) var = 0.0; else var /= count * (count - 1); /* Windows says sqrt(0.0) is "-1.#J" (?!) so we must test. */ sigma = (var != 0.) ? sqrt(var) : 0.; } fprintf(stderr, "%lu total strings, mean length %g (sigma %g)\n", (unsigned long)count, mean, sigma); mean = var = sigma = 0.; count = rt->totalDependentStrings; if (count > 0 && rt->strdepLengthSum >= 0) { mean = rt->strdepLengthSum / count; var = count * rt->strdepLengthSquaredSum - rt->strdepLengthSum * rt->strdepLengthSum; if (var < 0.0 || count <= 1) var = 0.0; else var /= count * (count - 1); /* Windows says sqrt(0.0) is "-1.#J" (?!) so we must test. */ sigma = (var != 0.) ? sqrt(var) : 0.; } fprintf(stderr, "%lu total dependent strings, mean length %g (sigma %g)\n", (unsigned long)count, mean, sigma); } #endif JSString * js_NewStringCopyN(JSContext *cx, const jschar *s, size_t n, uintN gcflag) { jschar *news; JSString *str; news = (jschar *)JS_malloc(cx, (n + 1) * sizeof(jschar)); if (!news) return NULL; js_strncpy(news, s, n); news[n] = 0; str = js_NewString(cx, news, n, gcflag); if (!str) JS_free(cx, news); return str; } JSString * js_NewStringCopyZ(JSContext *cx, const jschar *s, uintN gcflag) { size_t n, m; jschar *news; JSString *str; n = js_strlen(s); m = (n + 1) * sizeof(jschar); news = (jschar *) JS_malloc(cx, m); if (!news) return NULL; memcpy(news, s, m); str = js_NewString(cx, news, n, gcflag); if (!str) JS_free(cx, news); return str; } JS_STATIC_DLL_CALLBACK(JSHashNumber) js_hash_string_pointer(const void *key) { return (JSHashNumber)JS_PTR_TO_UINT32(key) >> JSVAL_TAGBITS; } void js_PurgeDeflatedStringCache(JSRuntime *rt, JSString *str) { JSHashNumber hash; JSHashEntry *he, **hep; if (!rt->deflatedStringCache) return; hash = js_hash_string_pointer(str); JS_ACQUIRE_LOCK(rt->deflatedStringCacheLock); hep = JS_HashTableRawLookup(rt->deflatedStringCache, hash, str); he = *hep; if (he) { #ifdef DEBUG rt->deflatedStringCacheBytes -= JSSTRING_LENGTH(str); #endif free(he->value); JS_HashTableRawRemove(rt->deflatedStringCache, hep, he); } JS_RELEASE_LOCK(rt->deflatedStringCacheLock); } void js_FinalizeString(JSContext *cx, JSString *str) { js_FinalizeStringRT(cx->runtime, str); } void js_FinalizeStringRT(JSRuntime *rt, JSString *str) { JSBool valid; JS_RUNTIME_UNMETER(rt, liveStrings); if (JSSTRING_IS_DEPENDENT(str)) { /* If JSSTRFLAG_DEPENDENT is set, this string must be valid. */ JS_ASSERT(JSSTRDEP_BASE(str)); JS_RUNTIME_UNMETER(rt, liveDependentStrings); valid = JS_TRUE; } else { /* A stillborn string has null chars, so is not valid. */ valid = (str->chars != NULL); if (valid) free(str->chars); } if (valid) { js_PurgeDeflatedStringCache(rt, str); str->chars = NULL; } str->length = 0; } JSObject * js_StringToObject(JSContext *cx, JSString *str) { JSObject *obj; obj = js_NewObject(cx, &js_StringClass, NULL, NULL); if (!obj) return NULL; OBJ_SET_SLOT(cx, obj, JSSLOT_PRIVATE, STRING_TO_JSVAL(str)); return obj; } JS_FRIEND_API(const char *) js_ValueToPrintable(JSContext *cx, jsval v, JSValueToStringFun v2sfun) { JSString *str; const char *bytes; str = v2sfun(cx, v); if (!str) return NULL; str = js_QuoteString(cx, str, 0); if (!str) return NULL; bytes = js_GetStringBytes(cx->runtime, str); if (!bytes) JS_ReportOutOfMemory(cx); return bytes; } JS_FRIEND_API(JSString *) js_ValueToString(JSContext *cx, jsval v) { JSObject *obj; JSString *str; if (JSVAL_IS_OBJECT(v)) { obj = JSVAL_TO_OBJECT(v); if (!obj) return ATOM_TO_STRING(cx->runtime->atomState.nullAtom); if (!OBJ_DEFAULT_VALUE(cx, obj, JSTYPE_STRING, &v)) return NULL; } if (JSVAL_IS_STRING(v)) { str = JSVAL_TO_STRING(v); } else if (JSVAL_IS_INT(v)) { str = js_NumberToString(cx, JSVAL_TO_INT(v)); } else if (JSVAL_IS_DOUBLE(v)) { str = js_NumberToString(cx, *JSVAL_TO_DOUBLE(v)); } else if (JSVAL_IS_BOOLEAN(v)) { str = js_BooleanToString(cx, JSVAL_TO_BOOLEAN(v)); } else { str = ATOM_TO_STRING(cx->runtime->atomState.typeAtoms[JSTYPE_VOID]); } return str; } JS_FRIEND_API(JSString *) js_ValueToSource(JSContext *cx, jsval v) { JSTempValueRooter tvr; JSString *str; if (JSVAL_IS_STRING(v)) return js_QuoteString(cx, JSVAL_TO_STRING(v), '"'); if (JSVAL_IS_PRIMITIVE(v)) { /* Special case to preserve negative zero, _contra_ toString. */ if (JSVAL_IS_DOUBLE(v) && JSDOUBLE_IS_NEGZERO(*JSVAL_TO_DOUBLE(v))) { /* NB: _ucNstr rather than _ucstr to indicate non-terminated. */ static const jschar js_negzero_ucNstr[] = {'-', '0'}; return js_NewStringCopyN(cx, js_negzero_ucNstr, 2, 0); } return js_ValueToString(cx, v); } JS_PUSH_SINGLE_TEMP_ROOT(cx, JSVAL_NULL, &tvr); if (!js_TryMethod(cx, JSVAL_TO_OBJECT(v), cx->runtime->atomState.toSourceAtom, 0, NULL, &tvr.u.value)) { str = NULL; } else { str = js_ValueToString(cx, tvr.u.value); } JS_POP_TEMP_ROOT(cx, &tvr); return str; } JSHashNumber js_HashString(JSString *str) { JSHashNumber h; const jschar *s; size_t n; h = 0; for (s = JSSTRING_CHARS(str), n = JSSTRING_LENGTH(str); n; s++, n--) h = (h >> (JS_HASH_BITS - 4)) ^ (h << 4) ^ *s; return h; } intN js_CompareStrings(JSString *str1, JSString *str2) { size_t l1, l2, n, i; const jschar *s1, *s2; intN cmp; JS_ASSERT(str1); JS_ASSERT(str2); /* Fast case: pointer equality could be a quick win. */ if (str1 == str2) return 0; l1 = JSSTRING_LENGTH(str1), l2 = JSSTRING_LENGTH(str2); s1 = JSSTRING_CHARS(str1), s2 = JSSTRING_CHARS(str2); n = JS_MIN(l1, l2); for (i = 0; i < n; i++) { cmp = s1[i] - s2[i]; if (cmp != 0) return cmp; } return (intN)(l1 - l2); } JSBool js_EqualStrings(JSString *str1, JSString *str2) { size_t n; const jschar *s1, *s2; JS_ASSERT(str1); JS_ASSERT(str2); /* Fast case: pointer equality could be a quick win. */ if (str1 == str2) return JS_TRUE; n = JSSTRING_LENGTH(str1); if (n != JSSTRING_LENGTH(str2)) return JS_FALSE; if (n == 0) return JS_TRUE; s1 = JSSTRING_CHARS(str1), s2 = JSSTRING_CHARS(str2); do { if (*s1 != *s2) return JS_FALSE; ++s1, ++s2; } while (--n != 0); return JS_TRUE; } size_t js_strlen(const jschar *s) { const jschar *t; for (t = s; *t != 0; t++) continue; return (size_t)(t - s); } jschar * js_strchr(const jschar *s, jschar c) { while (*s != 0) { if (*s == c) return (jschar *)s; s++; } return NULL; } jschar * js_strchr_limit(const jschar *s, jschar c, const jschar *limit) { while (s < limit) { if (*s == c) return (jschar *)s; s++; } return NULL; } const jschar * js_SkipWhiteSpace(const jschar *s) { /* JS_ISSPACE is false on a null. */ while (JS_ISSPACE(*s)) s++; return s; } #ifdef JS_C_STRINGS_ARE_UTF8 jschar * js_InflateString(JSContext *cx, const char *bytes, size_t *length) { jschar *chars = NULL; size_t dstlen = 0; if (!js_InflateStringToBuffer(cx, bytes, *length, NULL, &dstlen)) return NULL; chars = (jschar *) JS_malloc(cx, (dstlen + 1) * sizeof (jschar)); if (!chars) return NULL; js_InflateStringToBuffer(cx, bytes, *length, chars, &dstlen); chars[dstlen] = 0; *length = dstlen; return chars; } /* * May be called with null cx by js_GetStringBytes, see below. */ char * js_DeflateString(JSContext *cx, const jschar *chars, size_t length) { size_t size = 0; char *bytes = NULL; if (!js_DeflateStringToBuffer(cx, chars, length, NULL, &size)) return NULL; bytes = (char *) (cx ? JS_malloc(cx, size+1) : malloc(size+1)); if (!bytes) return NULL; js_DeflateStringToBuffer(cx, chars, length, bytes, &size); bytes[size] = 0; return bytes; } JSBool js_DeflateStringToBuffer(JSContext *cx, const jschar *src, size_t srclen, char *dst, size_t *dstlenp) { size_t i, utf8Len, dstlen = *dstlenp, origDstlen = dstlen; jschar c, c2; uint32 v; uint8 utf8buf[6]; if (!dst) dstlen = origDstlen = (size_t) -1; while (srclen) { c = *src++; srclen--; if ((c >= 0xDC00) && (c <= 0xDFFF)) goto badSurrogate; if (c < 0xD800 || c > 0xDBFF) { v = c; } else { if (srclen < 1) goto bufferTooSmall; c2 = *src++; srclen--; if ((c2 < 0xDC00) || (c2 > 0xDFFF)) { c = c2; goto badSurrogate; } v = ((c - 0xD800) << 10) + (c2 - 0xDC00) + 0x10000; } if (v < 0x0080) { /* no encoding necessary - performance hack */ if (!dstlen) goto bufferTooSmall; if (dst) *dst++ = (char) v; utf8Len = 1; } else { utf8Len = js_OneUcs4ToUtf8Char(utf8buf, v); if (utf8Len > dstlen) goto bufferTooSmall; if (dst) { for (i = 0; i < utf8Len; i++) *dst++ = (char) utf8buf[i]; } } dstlen -= utf8Len; } *dstlenp = (origDstlen - dstlen); return JS_TRUE; badSurrogate: *dstlenp = (origDstlen - dstlen); if (cx) { char buffer[10]; JS_snprintf(buffer, 10, "0x%x", c); JS_ReportErrorFlagsAndNumber(cx, JSREPORT_ERROR, js_GetErrorMessage, NULL, JSMSG_BAD_SURROGATE_CHAR, buffer); } return JS_FALSE; bufferTooSmall: *dstlenp = (origDstlen - dstlen); if (cx) { JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_BUFFER_TOO_SMALL); } return JS_FALSE; } JSBool js_InflateStringToBuffer(JSContext *cx, const char *src, size_t srclen, jschar *dst, size_t *dstlenp) { uint32 v; size_t offset = 0, j, n, dstlen = *dstlenp, origDstlen = dstlen; if (!dst) dstlen = origDstlen = (size_t) -1; while (srclen) { v = (uint8) *src; n = 1; if (v & 0x80) { while (v & (0x80 >> n)) n++; if (n > srclen) goto bufferTooSmall; if (n == 1 || n > 6) goto badCharacter; for (j = 1; j < n; j++) { if ((src[j] & 0xC0) != 0x80) goto badCharacter; } v = Utf8ToOneUcs4Char(src, n); if (v >= 0x10000) { v -= 0x10000; if (v > 0xFFFFF || dstlen < 2) { *dstlenp = (origDstlen - dstlen); if (cx) { char buffer[10]; JS_snprintf(buffer, 10, "0x%x", v + 0x10000); JS_ReportErrorFlagsAndNumber(cx, JSREPORT_ERROR, js_GetErrorMessage, NULL, JSMSG_UTF8_CHAR_TOO_LARGE, buffer); } return JS_FALSE; } if (dstlen < 2) goto bufferTooSmall; if (dst) { *dst++ = (jschar)((v >> 10) + 0xD800); v = (jschar)((v & 0x3FF) + 0xDC00); } dstlen--; } } if (!dstlen) goto bufferTooSmall; if (dst) *dst++ = (jschar) v; dstlen--; offset += n; src += n; srclen -= n; } *dstlenp = (origDstlen - dstlen); return JS_TRUE; badCharacter: *dstlenp = (origDstlen - dstlen); if (cx) { char buffer[10]; JS_snprintf(buffer, 10, "%d", offset); JS_ReportErrorFlagsAndNumber(cx, JSREPORT_ERROR, js_GetErrorMessage, NULL, JSMSG_MALFORMED_UTF8_CHAR, buffer); } return JS_FALSE; bufferTooSmall: *dstlenp = (origDstlen - dstlen); if (cx) { JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_BUFFER_TOO_SMALL); } return JS_FALSE; } #else JSBool js_InflateStringToBuffer(JSContext* cx, const char *bytes, size_t length, jschar *chars, size_t* charsLength) { size_t i; if (length > *charsLength) { for (i = 0; i < *charsLength; i++) chars[i] = (unsigned char) bytes[i]; if (cx) { JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_BUFFER_TOO_SMALL); } return JS_FALSE; } for (i = 0; i < length; i++) chars[i] = (unsigned char) bytes[i]; *charsLength = length; return JS_TRUE; } jschar * js_InflateString(JSContext *cx, const char *bytes, size_t *bytesLength) { jschar *chars; size_t i, length = *bytesLength; chars = (jschar *) JS_malloc(cx, (length + 1) * sizeof(jschar)); if (!chars) { *bytesLength = 0; return NULL; } for (i = 0; i < length; i++) chars[i] = (unsigned char) bytes[i]; chars[length] = 0; *bytesLength = length; return chars; } JSBool js_DeflateStringToBuffer(JSContext* cx, const jschar *chars, size_t length, char *bytes, size_t* bytesLength) { size_t i; if (length > *bytesLength) { for (i = 0; i < *bytesLength; i++) bytes[i] = (char) chars[i]; if (cx) { JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_BUFFER_TOO_SMALL); } return JS_FALSE; } for (i = 0; i < length; i++) bytes[i] = (char) chars[i]; *bytesLength = length; return JS_TRUE; } /* * May be called with null cx by js_GetStringBytes, see below. */ char * js_DeflateString(JSContext *cx, const jschar *chars, size_t length) { size_t i, size; char *bytes; size = (length + 1) * sizeof(char); bytes = (char *) (cx ? JS_malloc(cx, size) : malloc(size)); if (!bytes) return NULL; for (i = 0; i < length; i++) bytes[i] = (char) chars[i]; bytes[length] = 0; return bytes; } #endif static JSHashTable * GetDeflatedStringCache(JSRuntime *rt) { JSHashTable *cache; cache = rt->deflatedStringCache; if (!cache) { cache = JS_NewHashTable(8, js_hash_string_pointer, JS_CompareValues, JS_CompareValues, NULL, NULL); rt->deflatedStringCache = cache; } return cache; } JSBool js_SetStringBytes(JSRuntime *rt, JSString *str, char *bytes, size_t length) { JSHashTable *cache; JSBool ok; JSHashNumber hash; JSHashEntry **hep; JS_ACQUIRE_LOCK(rt->deflatedStringCacheLock); cache = GetDeflatedStringCache(rt); if (!cache) { ok = JS_FALSE; } else { hash = js_hash_string_pointer(str); hep = JS_HashTableRawLookup(cache, hash, str); JS_ASSERT(*hep == NULL); ok = JS_HashTableRawAdd(cache, hep, hash, str, bytes) != NULL; #ifdef DEBUG if (ok) rt->deflatedStringCacheBytes += length; #endif } JS_RELEASE_LOCK(rt->deflatedStringCacheLock); return ok; } char * js_GetStringBytes(JSRuntime *rt, JSString *str) { JSHashTable *cache; char *bytes; JSHashNumber hash; JSHashEntry *he, **hep; JS_ACQUIRE_LOCK(rt->deflatedStringCacheLock); cache = GetDeflatedStringCache(rt); if (!cache) { bytes = NULL; } else { hash = js_hash_string_pointer(str); hep = JS_HashTableRawLookup(cache, hash, str); he = *hep; if (he) { bytes = (char *) he->value; /* Try to catch failure to JS_ShutDown between runtime epochs. */ JS_ASSERT((*bytes == '\0' && JSSTRING_LENGTH(str) == 0) || *bytes == (char) JSSTRING_CHARS(str)[0]); } else { bytes = js_DeflateString(NULL, JSSTRING_CHARS(str), JSSTRING_LENGTH(str)); if (bytes) { if (JS_HashTableRawAdd(cache, hep, hash, str, bytes)) { #ifdef DEBUG rt->deflatedStringCacheBytes += JSSTRING_LENGTH(str); #endif } else { free(bytes); bytes = NULL; } } } } JS_RELEASE_LOCK(rt->deflatedStringCacheLock); return bytes; } /* * From java.lang.Character.java: * * The character properties are currently encoded into 32 bits in the * following manner: * * 10 bits signed offset used for converting case * 1 bit if 1, adding the signed offset converts the character to * lowercase * 1 bit if 1, subtracting the signed offset converts the character to * uppercase * 1 bit if 1, character has a titlecase equivalent (possibly itself) * 3 bits 0 may not be part of an identifier * 1 ignorable control; may continue a Unicode identifier or JS * identifier * 2 may continue a JS identifier but not a Unicode identifier * (unused) * 3 may continue a Unicode identifier or JS identifier * 4 is a JS whitespace character * 5 may start or continue a JS identifier; * may continue but not start a Unicode identifier (_) * 6 may start or continue a JS identifier but not a Unicode * identifier ($) * 7 may start or continue a Unicode identifier or JS identifier * Thus: * 5, 6, 7 may start a JS identifier * 1, 2, 3, 5, 6, 7 may continue a JS identifier * 7 may start a Unicode identifier * 1, 3, 5, 7 may continue a Unicode identifier * 1 is ignorable within an identifier * 4 is JS whitespace * 2 bits 0 this character has no numeric property * 1 adding the digit offset to the character code and then * masking with 0x1F will produce the desired numeric value * 2 this character has a "strange" numeric value * 3 a JS supradecimal digit: adding the digit offset to the * character code, then masking with 0x1F, then adding 10 * will produce the desired numeric value * 5 bits digit offset * 1 bit XML 1.0 name start character * 1 bit XML 1.0 name character * 2 bits reserved for future use * 5 bits character type */ /* The X table has 1024 entries for a total of 1024 bytes. */ const uint8 js_X[] = { 0, 1, 2, 3, 4, 5, 6, 7, /* 0x0000 */ 8, 9, 10, 11, 12, 13, 14, 15, /* 0x0200 */ 16, 17, 18, 19, 20, 21, 22, 23, /* 0x0400 */ 24, 25, 26, 27, 28, 28, 28, 28, /* 0x0600 */ 28, 28, 28, 28, 29, 30, 31, 32, /* 0x0800 */ 33, 34, 35, 36, 37, 38, 39, 40, /* 0x0A00 */ 41, 42, 43, 44, 45, 46, 28, 28, /* 0x0C00 */ 47, 48, 49, 50, 51, 52, 53, 28, /* 0x0E00 */ 28, 28, 54, 55, 56, 57, 58, 59, /* 0x1000 */ 28, 28, 28, 28, 28, 28, 28, 28, /* 0x1200 */ 28, 28, 28, 28, 28, 28, 28, 28, /* 0x1400 */ 28, 28, 28, 28, 28, 28, 28, 28, /* 0x1600 */ 28, 28, 28, 28, 28, 28, 28, 28, /* 0x1800 */ 28, 28, 28, 28, 28, 28, 28, 28, /* 0x1A00 */ 28, 28, 28, 28, 28, 28, 28, 28, /* 0x1C00 */ 60, 60, 61, 62, 63, 64, 65, 66, /* 0x1E00 */ 67, 68, 69, 70, 71, 72, 73, 74, /* 0x2000 */ 75, 75, 75, 76, 77, 78, 28, 28, /* 0x2200 */ 79, 80, 81, 82, 83, 83, 84, 85, /* 0x2400 */ 86, 85, 28, 28, 87, 88, 89, 28, /* 0x2600 */ 28, 28, 28, 28, 28, 28, 28, 28, /* 0x2800 */ 28, 28, 28, 28, 28, 28, 28, 28, /* 0x2A00 */ 28, 28, 28, 28, 28, 28, 28, 28, /* 0x2C00 */ 28, 28, 28, 28, 28, 28, 28, 28, /* 0x2E00 */ 90, 91, 92, 93, 94, 56, 95, 28, /* 0x3000 */ 96, 97, 98, 99, 83, 100, 83, 101, /* 0x3200 */ 28, 28, 28, 28, 28, 28, 28, 28, /* 0x3400 */ 28, 28, 28, 28, 28, 28, 28, 28, /* 0x3600 */ 28, 28, 28, 28, 28, 28, 28, 28, /* 0x3800 */ 28, 28, 28, 28, 28, 28, 28, 28, /* 0x3A00 */ 28, 28, 28, 28, 28, 28, 28, 28, /* 0x3C00 */ 28, 28, 28, 28, 28, 28, 28, 28, /* 0x3E00 */ 28, 28, 28, 28, 28, 28, 28, 28, /* 0x4000 */ 28, 28, 28, 28, 28, 28, 28, 28, /* 0x4200 */ 28, 28, 28, 28, 28, 28, 28, 28, /* 0x4400 */ 28, 28, 28, 28, 28, 28, 28, 28, /* 0x4600 */ 28, 28, 28, 28, 28, 28, 28, 28, /* 0x4800 */ 28, 28, 28, 28, 28, 28, 28, 28, /* 0x4A00 */ 28, 28, 28, 28, 28, 28, 28, 28, /* 0x4C00 */ 56, 56, 56, 56, 56, 56, 56, 56, /* 0x4E00 */ 56, 56, 56, 56, 56, 56, 56, 56, /* 0x5000 */ 56, 56, 56, 56, 56, 56, 56, 56, /* 0x5200 */ 56, 56, 56, 56, 56, 56, 56, 56, /* 0x5400 */ 56, 56, 56, 56, 56, 56, 56, 56, /* 0x5600 */ 56, 56, 56, 56, 56, 56, 56, 56, /* 0x5800 */ 56, 56, 56, 56, 56, 56, 56, 56, /* 0x5A00 */ 56, 56, 56, 56, 56, 56, 56, 56, /* 0x5C00 */ 56, 56, 56, 56, 56, 56, 56, 56, /* 0x5E00 */ 56, 56, 56, 56, 56, 56, 56, 56, /* 0x6000 */ 56, 56, 56, 56, 56, 56, 56, 56, /* 0x6200 */ 56, 56, 56, 56, 56, 56, 56, 56, /* 0x6400 */ 56, 56, 56, 56, 56, 56, 56, 56, /* 0x6600 */ 56, 56, 56, 56, 56, 56, 56, 56, /* 0x6800 */ 56, 56, 56, 56, 56, 56, 56, 56, /* 0x6A00 */ 56, 56, 56, 56, 56, 56, 56, 56, /* 0x6C00 */ 56, 56, 56, 56, 56, 56, 56, 56, /* 0x6E00 */ 56, 56, 56, 56, 56, 56, 56, 56, /* 0x7000 */ 56, 56, 56, 56, 56, 56, 56, 56, /* 0x7200 */ 56, 56, 56, 56, 56, 56, 56, 56, /* 0x7400 */ 56, 56, 56, 56, 56, 56, 56, 56, /* 0x7600 */ 56, 56, 56, 56, 56, 56, 56, 56, /* 0x7800 */ 56, 56, 56, 56, 56, 56, 56, 56, /* 0x7A00 */ 56, 56, 56, 56, 56, 56, 56, 56, /* 0x7C00 */ 56, 56, 56, 56, 56, 56, 56, 56, /* 0x7E00 */ 56, 56, 56, 56, 56, 56, 56, 56, /* 0x8000 */ 56, 56, 56, 56, 56, 56, 56, 56, /* 0x8200 */ 56, 56, 56, 56, 56, 56, 56, 56, /* 0x8400 */ 56, 56, 56, 56, 56, 56, 56, 56, /* 0x8600 */ 56, 56, 56, 56, 56, 56, 56, 56, /* 0x8800 */ 56, 56, 56, 56, 56, 56, 56, 56, /* 0x8A00 */ 56, 56, 56, 56, 56, 56, 56, 56, /* 0x8C00 */ 56, 56, 56, 56, 56, 56, 56, 56, /* 0x8E00 */ 56, 56, 56, 56, 56, 56, 56, 56, /* 0x9000 */ 56, 56, 56, 56, 56, 56, 56, 56, /* 0x9200 */ 56, 56, 56, 56, 56, 56, 56, 56, /* 0x9400 */ 56, 56, 56, 56, 56, 56, 56, 56, /* 0x9600 */ 56, 56, 56, 56, 56, 56, 56, 56, /* 0x9800 */ 56, 56, 56, 56, 56, 56, 56, 56, /* 0x9A00 */ 56, 56, 56, 56, 56, 56, 56, 56, /* 0x9C00 */ 56, 56, 56, 56, 56, 56, 102, 28, /* 0x9E00 */ 28, 28, 28, 28, 28, 28, 28, 28, /* 0xA000 */ 28, 28, 28, 28, 28, 28, 28, 28, /* 0xA200 */ 28, 28, 28, 28, 28, 28, 28, 28, /* 0xA400 */ 28, 28, 28, 28, 28, 28, 28, 28, /* 0xA600 */ 28, 28, 28, 28, 28, 28, 28, 28, /* 0xA800 */ 28, 28, 28, 28, 28, 28, 28, 28, /* 0xAA00 */ 56, 56, 56, 56, 56, 56, 56, 56, /* 0xAC00 */ 56, 56, 56, 56, 56, 56, 56, 56, /* 0xAE00 */ 56, 56, 56, 56, 56, 56, 56, 56, /* 0xB000 */ 56, 56, 56, 56, 56, 56, 56, 56, /* 0xB200 */ 56, 56, 56, 56, 56, 56, 56, 56, /* 0xB400 */ 56, 56, 56, 56, 56, 56, 56, 56, /* 0xB600 */ 56, 56, 56, 56, 56, 56, 56, 56, /* 0xB800 */ 56, 56, 56, 56, 56, 56, 56, 56, /* 0xBA00 */ 56, 56, 56, 56, 56, 56, 56, 56, /* 0xBC00 */ 56, 56, 56, 56, 56, 56, 56, 56, /* 0xBE00 */ 56, 56, 56, 56, 56, 56, 56, 56, /* 0xC000 */ 56, 56, 56, 56, 56, 56, 56, 56, /* 0xC200 */ 56, 56, 56, 56, 56, 56, 56, 56, /* 0xC400 */ 56, 56, 56, 56, 56, 56, 56, 56, /* 0xC600 */ 56, 56, 56, 56, 56, 56, 56, 56, /* 0xC800 */ 56, 56, 56, 56, 56, 56, 56, 56, /* 0xCA00 */ 56, 56, 56, 56, 56, 56, 56, 56, /* 0xCC00 */ 56, 56, 56, 56, 56, 56, 56, 56, /* 0xCE00 */ 56, 56, 56, 56, 56, 56, 56, 56, /* 0xD000 */ 56, 56, 56, 56, 56, 56, 56, 56, /* 0xD200 */ 56, 56, 56, 56, 56, 56, 56, 56, /* 0xD400 */ 56, 56, 56, 56, 56, 56, 103, 28, /* 0xD600 */ 104, 104, 104, 104, 104, 104, 104, 104, /* 0xD800 */ 104, 104, 104, 104, 104, 104, 104, 104, /* 0xDA00 */ 104, 104, 104, 104, 104, 104, 104, 104, /* 0xDC00 */ 104, 104, 104, 104, 104, 104, 104, 104, /* 0xDE00 */ 105, 105, 105, 105, 105, 105, 105, 105, /* 0xE000 */ 105, 105, 105, 105, 105, 105, 105, 105, /* 0xE200 */ 105, 105, 105, 105, 105, 105, 105, 105, /* 0xE400 */ 105, 105, 105, 105, 105, 105, 105, 105, /* 0xE600 */ 105, 105, 105, 105, 105, 105, 105, 105, /* 0xE800 */ 105, 105, 105, 105, 105, 105, 105, 105, /* 0xEA00 */ 105, 105, 105, 105, 105, 105, 105, 105, /* 0xEC00 */ 105, 105, 105, 105, 105, 105, 105, 105, /* 0xEE00 */ 105, 105, 105, 105, 105, 105, 105, 105, /* 0xF000 */ 105, 105, 105, 105, 105, 105, 105, 105, /* 0xF200 */ 105, 105, 105, 105, 105, 105, 105, 105, /* 0xF400 */ 105, 105, 105, 105, 105, 105, 105, 105, /* 0xF600 */ 105, 105, 105, 105, 56, 56, 56, 56, /* 0xF800 */ 106, 28, 28, 28, 107, 108, 109, 110, /* 0xFA00 */ 56, 56, 56, 56, 111, 112, 113, 114, /* 0xFC00 */ 115, 116, 56, 117, 118, 119, 120, 121 /* 0xFE00 */ }; /* The Y table has 7808 entries for a total of 7808 bytes. */ const uint8 js_Y[] = { 0, 0, 0, 0, 0, 0, 0, 0, /* 0 */ 0, 1, 1, 1, 1, 1, 0, 0, /* 0 */ 0, 0, 0, 0, 0, 0, 0, 0, /* 0 */ 0, 0, 0, 0, 0, 0, 0, 0, /* 0 */ 2, 3, 3, 3, 4, 3, 3, 3, /* 0 */ 5, 6, 3, 7, 3, 8, 3, 3, /* 0 */ 9, 9, 9, 9, 9, 9, 9, 9, /* 0 */ 9, 9, 3, 3, 7, 7, 7, 3, /* 0 */ 3, 10, 10, 10, 10, 10, 10, 10, /* 1 */ 10, 10, 10, 10, 10, 10, 10, 10, /* 1 */ 10, 10, 10, 10, 10, 10, 10, 10, /* 1 */ 10, 10, 10, 5, 3, 6, 11, 12, /* 1 */ 11, 13, 13, 13, 13, 13, 13, 13, /* 1 */ 13, 13, 13, 13, 13, 13, 13, 13, /* 1 */ 13, 13, 13, 13, 13, 13, 13, 13, /* 1 */ 13, 13, 13, 5, 7, 6, 7, 0, /* 1 */ 0, 0, 0, 0, 0, 0, 0, 0, /* 2 */ 0, 0, 0, 0, 0, 0, 0, 0, /* 2 */ 0, 0, 0, 0, 0, 0, 0, 0, /* 2 */ 0, 0, 0, 0, 0, 0, 0, 0, /* 2 */ 2, 3, 4, 4, 4, 4, 15, 15, /* 2 */ 11, 15, 16, 5, 7, 8, 15, 11, /* 2 */ 15, 7, 17, 17, 11, 16, 15, 3, /* 2 */ 11, 18, 16, 6, 19, 19, 19, 3, /* 2 */ 20, 20, 20, 20, 20, 20, 20, 20, /* 3 */ 20, 20, 20, 20, 20, 20, 20, 20, /* 3 */ 20, 20, 20, 20, 20, 20, 20, 7, /* 3 */ 20, 20, 20, 20, 20, 20, 20, 16, /* 3 */ 21, 21, 21, 21, 21, 21, 21, 21, /* 3 */ 21, 21, 21, 21, 21, 21, 21, 21, /* 3 */ 21, 21, 21, 21, 21, 21, 21, 7, /* 3 */ 21, 21, 21, 21, 21, 21, 21, 22, /* 3 */ 23, 24, 23, 24, 23, 24, 23, 24, /* 4 */ 23, 24, 23, 24, 23, 24, 23, 24, /* 4 */ 23, 24, 23, 24, 23, 24, 23, 24, /* 4 */ 23, 24, 23, 24, 23, 24, 23, 24, /* 4 */ 23, 24, 23, 24, 23, 24, 23, 24, /* 4 */ 23, 24, 23, 24, 23, 24, 23, 24, /* 4 */ 25, 26, 23, 24, 23, 24, 23, 24, /* 4 */ 16, 23, 24, 23, 24, 23, 24, 23, /* 4 */ 24, 23, 24, 23, 24, 23, 24, 23, /* 5 */ 24, 16, 23, 24, 23, 24, 23, 24, /* 5 */ 23, 24, 23, 24, 23, 24, 23, 24, /* 5 */ 23, 24, 23, 24, 23, 24, 23, 24, /* 5 */ 23, 24, 23, 24, 23, 24, 23, 24, /* 5 */ 23, 24, 23, 24, 23, 24, 23, 24, /* 5 */ 23, 24, 23, 24, 23, 24, 23, 24, /* 5 */ 27, 23, 24, 23, 24, 23, 24, 28, /* 5 */ 16, 29, 23, 24, 23, 24, 30, 23, /* 6 */ 24, 31, 31, 23, 24, 16, 32, 32, /* 6 */ 33, 23, 24, 31, 34, 16, 35, 36, /* 6 */ 23, 24, 16, 16, 35, 37, 16, 38, /* 6 */ 23, 24, 23, 24, 23, 24, 38, 23, /* 6 */ 24, 39, 40, 16, 23, 24, 39, 23, /* 6 */ 24, 41, 41, 23, 24, 23, 24, 42, /* 6 */ 23, 24, 16, 40, 23, 24, 40, 40, /* 6 */ 40, 40, 40, 40, 43, 44, 45, 43, /* 7 */ 44, 45, 43, 44, 45, 23, 24, 23, /* 7 */ 24, 23, 24, 23, 24, 23, 24, 23, /* 7 */ 24, 23, 24, 23, 24, 16, 23, 24, /* 7 */ 23, 24, 23, 24, 23, 24, 23, 24, /* 7 */ 23, 24, 23, 24, 23, 24, 23, 24, /* 7 */ 16, 43, 44, 45, 23, 24, 46, 46, /* 7 */ 46, 46, 23, 24, 23, 24, 23, 24, /* 7 */ 23, 24, 23, 24, 23, 24, 23, 24, /* 8 */ 23, 24, 23, 24, 23, 24, 23, 24, /* 8 */ 23, 24, 23, 24, 23, 24, 23, 24, /* 8 */ 46, 46, 46, 46, 46, 46, 46, 46, /* 8 */ 46, 46, 46, 46, 46, 46, 46, 46, /* 8 */ 46, 46, 46, 46, 46, 46, 46, 46, /* 8 */ 46, 46, 46, 46, 46, 46, 46, 46, /* 8 */ 46, 46, 46, 46, 46, 46, 46, 46, /* 8 */ 46, 46, 46, 46, 46, 46, 46, 46, /* 9 */ 46, 46, 46, 46, 46, 46, 46, 46, /* 9 */ 16, 16, 16, 47, 48, 16, 49, 49, /* 9 */ 50, 50, 16, 51, 16, 16, 16, 16, /* 9 */ 49, 16, 16, 52, 16, 16, 16, 16, /* 9 */ 53, 54, 16, 16, 16, 16, 16, 54, /* 9 */ 16, 16, 55, 16, 16, 16, 16, 16, /* 9 */ 16, 16, 16, 16, 16, 16, 16, 16, /* 9 */ 16, 16, 16, 56, 16, 16, 16, 16, /* 10 */ 56, 16, 57, 57, 16, 16, 16, 16, /* 10 */ 16, 16, 58, 16, 16, 16, 16, 16, /* 10 */ 16, 16, 16, 16, 16, 16, 16, 16, /* 10 */ 16, 16, 16, 16, 16, 16, 16, 16, /* 10 */ 16, 46, 46, 46, 46, 46, 46, 46, /* 10 */ 59, 59, 59, 59, 59, 59, 59, 59, /* 10 */ 59, 11, 11, 59, 59, 59, 59, 59, /* 10 */ 59, 59, 11, 11, 11, 11, 11, 11, /* 11 */ 11, 11, 11, 11, 11, 11, 11, 11, /* 11 */ 59, 59, 11, 11, 11, 11, 11, 11, /* 11 */ 11, 11, 11, 11, 11, 11, 11, 46, /* 11 */ 59, 59, 59, 59, 59, 11, 11, 11, /* 11 */ 11, 11, 46, 46, 46, 46, 46, 46, /* 11 */ 46, 46, 46, 46, 46, 46, 46, 46, /* 11 */ 46, 46, 46, 46, 46, 46, 46, 46, /* 11 */ 60, 60, 60, 60, 60, 60, 60, 60, /* 12 */ 60, 60, 60, 60, 60, 60, 60, 60, /* 12 */ 60, 60, 60, 60, 60, 60, 60, 60, /* 12 */ 60, 60, 60, 60, 60, 60, 60, 60, /* 12 */ 60, 60, 60, 60, 60, 60, 60, 60, /* 12 */ 60, 60, 60, 60, 60, 60, 60, 60, /* 12 */ 60, 60, 60, 60, 60, 60, 60, 60, /* 12 */ 60, 60, 60, 60, 60, 60, 60, 60, /* 12 */ 60, 60, 60, 60, 60, 60, 46, 46, /* 13 */ 46, 46, 46, 46, 46, 46, 46, 46, /* 13 */ 46, 46, 46, 46, 46, 46, 46, 46, /* 13 */ 46, 46, 46, 46, 46, 46, 46, 46, /* 13 */ 60, 60, 46, 46, 46, 46, 46, 46, /* 13 */ 46, 46, 46, 46, 46, 46, 46, 46, /* 13 */ 46, 46, 46, 46, 3, 3, 46, 46, /* 13 */ 46, 46, 59, 46, 46, 46, 3, 46, /* 13 */ 46, 46, 46, 46, 11, 11, 61, 3, /* 14 */ 62, 62, 62, 46, 63, 46, 64, 64, /* 14 */ 16, 20, 20, 20, 20, 20, 20, 20, /* 14 */ 20, 20, 20, 20, 20, 20, 20, 20, /* 14 */ 20, 20, 46, 20, 20, 20, 20, 20, /* 14 */ 20, 20, 20, 20, 65, 66, 66, 66, /* 14 */ 16, 21, 21, 21, 21, 21, 21, 21, /* 14 */ 21, 21, 21, 21, 21, 21, 21, 21, /* 14 */ 21, 21, 16, 21, 21, 21, 21, 21, /* 15 */ 21, 21, 21, 21, 67, 68, 68, 46, /* 15 */ 69, 70, 38, 38, 38, 71, 72, 46, /* 15 */ 46, 46, 38, 46, 38, 46, 38, 46, /* 15 */ 38, 46, 23, 24, 23, 24, 23, 24, /* 15 */ 23, 24, 23, 24, 23, 24, 23, 24, /* 15 */ 73, 74, 16, 40, 46, 46, 46, 46, /* 15 */ 46, 46, 46, 46, 46, 46, 46, 46, /* 15 */ 46, 75, 75, 75, 75, 75, 75, 75, /* 16 */ 75, 75, 75, 75, 75, 46, 75, 75, /* 16 */ 20, 20, 20, 20, 20, 20, 20, 20, /* 16 */ 20, 20, 20, 20, 20, 20, 20, 20, /* 16 */ 20, 20, 20, 20, 20, 20, 20, 20, /* 16 */ 20, 20, 20, 20, 20, 20, 20, 20, /* 16 */ 21, 21, 21, 21, 21, 21, 21, 21, /* 16 */ 21, 21, 21, 21, 21, 21, 21, 21, /* 16 */ 21, 21, 21, 21, 21, 21, 21, 21, /* 17 */ 21, 21, 21, 21, 21, 21, 21, 21, /* 17 */ 46, 74, 74, 74, 74, 74, 74, 74, /* 17 */ 74, 74, 74, 74, 74, 46, 74, 74, /* 17 */ 23, 24, 23, 24, 23, 24, 23, 24, /* 17 */ 23, 24, 23, 24, 23, 24, 23, 24, /* 17 */ 23, 24, 23, 24, 23, 24, 23, 24, /* 17 */ 23, 24, 23, 24, 23, 24, 23, 24, /* 17 */ 23, 24, 15, 60, 60, 60, 60, 46, /* 18 */ 46, 46, 46, 46, 46, 46, 46, 46, /* 18 */ 23, 24, 23, 24, 23, 24, 23, 24, /* 18 */ 23, 24, 23, 24, 23, 24, 23, 24, /* 18 */ 23, 24, 23, 24, 23, 24, 23, 24, /* 18 */ 23, 24, 23, 24, 23, 24, 23, 24, /* 18 */ 23, 24, 23, 24, 23, 24, 23, 24, /* 18 */ 23, 24, 23, 24, 23, 24, 23, 24, /* 18 */ 40, 23, 24, 23, 24, 46, 46, 23, /* 19 */ 24, 46, 46, 23, 24, 46, 46, 46, /* 19 */ 23, 24, 23, 24, 23, 24, 23, 24, /* 19 */ 23, 24, 23, 24, 23, 24, 23, 24, /* 19 */ 23, 24, 23, 24, 23, 24, 23, 24, /* 19 */ 23, 24, 23, 24, 46, 46, 23, 24, /* 19 */ 23, 24, 23, 24, 23, 24, 46, 46, /* 19 */ 23, 24, 46, 46, 46, 46, 46, 46, /* 19 */ 46, 46, 46, 46, 46, 46, 46, 46, /* 20 */ 46, 46, 46, 46, 46, 46, 46, 46, /* 20 */ 46, 46, 46, 46, 46, 46, 46, 46, /* 20 */ 46, 46, 46, 46, 46, 46, 46, 46, /* 20 */ 46, 46, 46, 46, 46, 46, 46, 46, /* 20 */ 46, 46, 46, 46, 46, 46, 46, 46, /* 20 */ 46, 76, 76, 76, 76, 76, 76, 76, /* 20 */ 76, 76, 76, 76, 76, 76, 76, 76, /* 20 */ 76, 76, 76, 76, 76, 76, 76, 76, /* 21 */ 76, 76, 76, 76, 76, 76, 76, 76, /* 21 */ 76, 76, 76, 76, 76, 76, 76, 46, /* 21 */ 46, 59, 3, 3, 3, 3, 3, 3, /* 21 */ 46, 77, 77, 77, 77, 77, 77, 77, /* 21 */ 77, 77, 77, 77, 77, 77, 77, 77, /* 21 */ 77, 77, 77, 77, 77, 77, 77, 77, /* 21 */ 77, 77, 77, 77, 77, 77, 77, 77, /* 21 */ 77, 77, 77, 77, 77, 77, 77, 16, /* 22 */ 46, 3, 46, 46, 46, 46, 46, 46, /* 22 */ 46, 60, 60, 60, 60, 60, 60, 60, /* 22 */ 60, 60, 60, 60, 60, 60, 60, 60, /* 22 */ 60, 60, 46, 60, 60, 60, 60, 60, /* 22 */ 60, 60, 60, 60, 60, 60, 60, 60, /* 22 */ 60, 60, 60, 60, 60, 60, 60, 60, /* 22 */ 60, 60, 46, 60, 60, 60, 3, 60, /* 22 */ 3, 60, 60, 3, 60, 46, 46, 46, /* 23 */ 46, 46, 46, 46, 46, 46, 46, 46, /* 23 */ 40, 40, 40, 40, 40, 40, 40, 40, /* 23 */ 40, 40, 40, 40, 40, 40, 40, 40, /* 23 */ 40, 40, 40, 40, 40, 40, 40, 40, /* 23 */ 40, 40, 40, 46, 46, 46, 46, 46, /* 23 */ 40, 40, 40, 3, 3, 46, 46, 46, /* 23 */ 46, 46, 46, 46, 46, 46, 46, 46, /* 23 */ 46, 46, 46, 46, 46, 46, 46, 46, /* 24 */ 46, 46, 46, 46, 3, 46, 46, 46, /* 24 */ 46, 46, 46, 46, 46, 46, 46, 46, /* 24 */ 46, 46, 46, 3, 46, 46, 46, 3, /* 24 */ 46, 40, 40, 40, 40, 40, 40, 40, /* 24 */ 40, 40, 40, 40, 40, 40, 40, 40, /* 24 */ 40, 40, 40, 40, 40, 40, 40, 40, /* 24 */ 40, 40, 40, 46, 46, 46, 46, 46, /* 24 */ 59, 40, 40, 40, 40, 40, 40, 40, /* 25 */ 40, 40, 40, 60, 60, 60, 60, 60, /* 25 */ 60, 60, 60, 46, 46, 46, 46, 46, /* 25 */ 46, 46, 46, 46, 46, 46, 46, 46, /* 25 */ 78, 78, 78, 78, 78, 78, 78, 78, /* 25 */ 78, 78, 3, 3, 3, 3, 46, 46, /* 25 */ 60, 40, 40, 40, 40, 40, 40, 40, /* 25 */ 40, 40, 40, 40, 40, 40, 40, 40, /* 25 */ 40, 40, 40, 40, 40, 40, 40, 40, /* 26 */ 40, 40, 40, 40, 40, 40, 40, 40, /* 26 */ 40, 40, 40, 40, 40, 40, 40, 40, /* 26 */ 40, 40, 40, 40, 40, 40, 40, 40, /* 26 */ 40, 40, 40, 40, 40, 40, 40, 40, /* 26 */ 40, 40, 40, 40, 40, 40, 40, 40, /* 26 */ 40, 40, 40, 40, 40, 40, 40, 40, /* 26 */ 46, 46, 40, 40, 40, 40, 40, 46, /* 26 */ 40, 40, 40, 40, 40, 40, 40, 40, /* 27 */ 40, 40, 40, 40, 40, 40, 40, 46, /* 27 */ 40, 40, 40, 40, 3, 40, 60, 60, /* 27 */ 60, 60, 60, 60, 60, 79, 79, 60, /* 27 */ 60, 60, 60, 60, 60, 59, 59, 60, /* 27 */ 60, 15, 60, 60, 60, 60, 46, 46, /* 27 */ 9, 9, 9, 9, 9, 9, 9, 9, /* 27 */ 9, 9, 46, 46, 46, 46, 46, 46, /* 27 */ 46, 46, 46, 46, 46, 46, 46, 46, /* 28 */ 46, 46, 46, 46, 46, 46, 46, 46, /* 28 */ 46, 46, 46, 46, 46, 46, 46, 46, /* 28 */ 46, 46, 46, 46, 46, 46, 46, 46, /* 28 */ 46, 46, 46, 46, 46, 46, 46, 46, /* 28 */ 46, 46, 46, 46, 46, 46, 46, 46, /* 28 */ 46, 46, 46, 46, 46, 46, 46, 46, /* 28 */ 46, 46, 46, 46, 46, 46, 46, 46, /* 28 */ 46, 60, 60, 80, 46, 40, 40, 40, /* 29 */ 40, 40, 40, 40, 40, 40, 40, 40, /* 29 */ 40, 40, 40, 40, 40, 40, 40, 40, /* 29 */ 40, 40, 40, 40, 40, 40, 40, 40, /* 29 */ 40, 40, 40, 40, 40, 40, 40, 40, /* 29 */ 40, 40, 40, 40, 40, 40, 40, 40, /* 29 */ 40, 40, 40, 40, 40, 40, 40, 40, /* 29 */ 40, 40, 46, 46, 60, 40, 80, 80, /* 29 */ 80, 60, 60, 60, 60, 60, 60, 60, /* 30 */ 60, 80, 80, 80, 80, 60, 46, 46, /* 30 */ 15, 60, 60, 60, 60, 46, 46, 46, /* 30 */ 40, 40, 40, 40, 40, 40, 40, 40, /* 30 */ 40, 40, 60, 60, 3, 3, 81, 81, /* 30 */ 81, 81, 81, 81, 81, 81, 81, 81, /* 30 */ 3, 46, 46, 46, 46, 46, 46, 46, /* 30 */ 46, 46, 46, 46, 46, 46, 46, 46, /* 30 */ 46, 60, 80, 80, 46, 40, 40, 40, /* 31 */ 40, 40, 40, 40, 40, 46, 46, 40, /* 31 */ 40, 46, 46, 40, 40, 40, 40, 40, /* 31 */ 40, 40, 40, 40, 40, 40, 40, 40, /* 31 */ 40, 40, 40, 40, 40, 40, 40, 40, /* 31 */ 40, 46, 40, 40, 40, 40, 40, 40, /* 31 */ 40, 46, 40, 46, 46, 46, 40, 40, /* 31 */ 40, 40, 46, 46, 60, 46, 80, 80, /* 31 */ 80, 60, 60, 60, 60, 46, 46, 80, /* 32 */ 80, 46, 46, 80, 80, 60, 46, 46, /* 32 */ 46, 46, 46, 46, 46, 46, 46, 80, /* 32 */ 46, 46, 46, 46, 40, 40, 46, 40, /* 32 */ 40, 40, 60, 60, 46, 46, 81, 81, /* 32 */ 81, 81, 81, 81, 81, 81, 81, 81, /* 32 */ 40, 40, 4, 4, 82, 82, 82, 82, /* 32 */ 19, 83, 15, 46, 46, 46, 46, 46, /* 32 */ 46, 46, 60, 46, 46, 40, 40, 40, /* 33 */ 40, 40, 40, 46, 46, 46, 46, 40, /* 33 */ 40, 46, 46, 40, 40, 40, 40, 40, /* 33 */ 40, 40, 40, 40, 40, 40, 40, 40, /* 33 */ 40, 40, 40, 40, 40, 40, 40, 40, /* 33 */ 40, 46, 40, 40, 40, 40, 40, 40, /* 33 */ 40, 46, 40, 40, 46, 40, 40, 46, /* 33 */ 40, 40, 46, 46, 60, 46, 80, 80, /* 33 */ 80, 60, 60, 46, 46, 46, 46, 60, /* 34 */ 60, 46, 46, 60, 60, 60, 46, 46, /* 34 */ 46, 46, 46, 46, 46, 46, 46, 46, /* 34 */ 46, 40, 40, 40, 40, 46, 40, 46, /* 34 */ 46, 46, 46, 46, 46, 46, 81, 81, /* 34 */ 81, 81, 81, 81, 81, 81, 81, 81, /* 34 */ 60, 60, 40, 40, 40, 46, 46, 46, /* 34 */ 46, 46, 46, 46, 46, 46, 46, 46, /* 34 */ 46, 60, 60, 80, 46, 40, 40, 40, /* 35 */ 40, 40, 40, 40, 46, 40, 46, 40, /* 35 */ 40, 40, 46, 40, 40, 40, 40, 40, /* 35 */ 40, 40, 40, 40, 40, 40, 40, 40, /* 35 */ 40, 40, 40, 40, 40, 40, 40, 40, /* 35 */ 40, 46, 40, 40, 40, 40, 40, 40, /* 35 */ 40, 46, 40, 40, 46, 40, 40, 40, /* 35 */ 40, 40, 46, 46, 60, 40, 80, 80, /* 35 */ 80, 60, 60, 60, 60, 60, 46, 60, /* 36 */ 60, 80, 46, 80, 80, 60, 46, 46, /* 36 */ 15, 46, 46, 46, 46, 46, 46, 46, /* 36 */ 46, 46, 46, 46, 46, 46, 46, 46, /* 36 */ 40, 46, 46, 46, 46, 46, 81, 81, /* 36 */ 81, 81, 81, 81, 81, 81, 81, 81, /* 36 */ 46, 46, 46, 46, 46, 46, 46, 46, /* 36 */ 46, 46, 46, 46, 46, 46, 46, 46, /* 36 */ 46, 60, 80, 80, 46, 40, 40, 40, /* 37 */ 40, 40, 40, 40, 40, 46, 46, 40, /* 37 */ 40, 46, 46, 40, 40, 40, 40, 40, /* 37 */ 40, 40, 40, 40, 40, 40, 40, 40, /* 37 */ 40, 40, 40, 40, 40, 40, 40, 40, /* 37 */ 40, 46, 40, 40, 40, 40, 40, 40, /* 37 */ 40, 46, 40, 40, 46, 46, 40, 40, /* 37 */ 40, 40, 46, 46, 60, 40, 80, 60, /* 37 */ 80, 60, 60, 60, 46, 46, 46, 80, /* 38 */ 80, 46, 46, 80, 80, 60, 46, 46, /* 38 */ 46, 46, 46, 46, 46, 46, 60, 80, /* 38 */ 46, 46, 46, 46, 40, 40, 46, 40, /* 38 */ 40, 40, 46, 46, 46, 46, 81, 81, /* 38 */ 81, 81, 81, 81, 81, 81, 81, 81, /* 38 */ 15, 46, 46, 46, 46, 46, 46, 46, /* 38 */ 46, 46, 46, 46, 46, 46, 46, 46, /* 38 */ 46, 46, 60, 80, 46, 40, 40, 40, /* 39 */ 40, 40, 40, 46, 46, 46, 40, 40, /* 39 */ 40, 46, 40, 40, 40, 40, 46, 46, /* 39 */ 46, 40, 40, 46, 40, 46, 40, 40, /* 39 */ 46, 46, 46, 40, 40, 46, 46, 46, /* 39 */ 40, 40, 40, 46, 46, 46, 40, 40, /* 39 */ 40, 40, 40, 40, 40, 40, 46, 40, /* 39 */ 40, 40, 46, 46, 46, 46, 80, 80, /* 39 */ 60, 80, 80, 46, 46, 46, 80, 80, /* 40 */ 80, 46, 80, 80, 80, 60, 46, 46, /* 40 */ 46, 46, 46, 46, 46, 46, 46, 80, /* 40 */ 46, 46, 46, 46, 46, 46, 46, 46, /* 40 */ 46, 46, 46, 46, 46, 46, 46, 81, /* 40 */ 81, 81, 81, 81, 81, 81, 81, 81, /* 40 */ 84, 19, 19, 46, 46, 46, 46, 46, /* 40 */ 46, 46, 46, 46, 46, 46, 46, 46, /* 40 */ 46, 80, 80, 80, 46, 40, 40, 40, /* 41 */ 40, 40, 40, 40, 40, 46, 40, 40, /* 41 */ 40, 46, 40, 40, 40, 40, 40, 40, /* 41 */ 40, 40, 40, 40, 40, 40, 40, 40, /* 41 */ 40, 40, 40, 40, 40, 40, 40, 40, /* 41 */ 40, 46, 40, 40, 40, 40, 40, 40, /* 41 */ 40, 40, 40, 40, 46, 40, 40, 40, /* 41 */ 40, 40, 46, 46, 46, 46, 60, 60, /* 41 */ 60, 80, 80, 80, 80, 46, 60, 60, /* 42 */ 60, 46, 60, 60, 60, 60, 46, 46, /* 42 */ 46, 46, 46, 46, 46, 60, 60, 46, /* 42 */ 46, 46, 46, 46, 46, 46, 46, 46, /* 42 */ 40, 40, 46, 46, 46, 46, 81, 81, /* 42 */ 81, 81, 81, 81, 81, 81, 81, 81, /* 42 */ 46, 46, 46, 46, 46, 46, 46, 46, /* 42 */ 46, 46, 46, 46, 46, 46, 46, 46, /* 42 */ 46, 46, 80, 80, 46, 40, 40, 40, /* 43 */ 40, 40, 40, 40, 40, 46, 40, 40, /* 43 */ 40, 46, 40, 40, 40, 40, 40, 40, /* 43 */ 40, 40, 40, 40, 40, 40, 40, 40, /* 43 */ 40, 40, 40, 40, 40, 40, 40, 40, /* 43 */ 40, 46, 40, 40, 40, 40, 40, 40, /* 43 */ 40, 40, 40, 40, 46, 40, 40, 40, /* 43 */ 40, 40, 46, 46, 46, 46, 80, 60, /* 43 */ 80, 80, 80, 80, 80, 46, 60, 80, /* 44 */ 80, 46, 80, 80, 60, 60, 46, 46, /* 44 */ 46, 46, 46, 46, 46, 80, 80, 46, /* 44 */ 46, 46, 46, 46, 46, 46, 40, 46, /* 44 */ 40, 40, 46, 46, 46, 46, 81, 81, /* 44 */ 81, 81, 81, 81, 81, 81, 81, 81, /* 44 */ 46, 46, 46, 46, 46, 46, 46, 46, /* 44 */ 46, 46, 46, 46, 46, 46, 46, 46, /* 44 */ 46, 46, 80, 80, 46, 40, 40, 40, /* 45 */ 40, 40, 40, 40, 40, 46, 40, 40, /* 45 */ 40, 46, 40, 40, 40, 40, 40, 40, /* 45 */ 40, 40, 40, 40, 40, 40, 40, 40, /* 45 */ 40, 40, 40, 40, 40, 40, 40, 40, /* 45 */ 40, 46, 40, 40, 40, 40, 40, 40, /* 45 */ 40, 40, 40, 40, 40, 40, 40, 40, /* 45 */ 40, 40, 46, 46, 46, 46, 80, 80, /* 45 */ 80, 60, 60, 60, 46, 46, 80, 80, /* 46 */ 80, 46, 80, 80, 80, 60, 46, 46, /* 46 */ 46, 46, 46, 46, 46, 46, 46, 80, /* 46 */ 46, 46, 46, 46, 46, 46, 46, 46, /* 46 */ 40, 40, 46, 46, 46, 46, 81, 81, /* 46 */ 81, 81, 81, 81, 81, 81, 81, 81, /* 46 */ 46, 46, 46, 46, 46, 46, 46, 46, /* 46 */ 46, 46, 46, 46, 46, 46, 46, 46, /* 46 */ 46, 40, 40, 40, 40, 40, 40, 40, /* 47 */ 40, 40, 40, 40, 40, 40, 40, 40, /* 47 */ 40, 40, 40, 40, 40, 40, 40, 40, /* 47 */ 40, 40, 40, 40, 40, 40, 40, 40, /* 47 */ 40, 40, 40, 40, 40, 40, 40, 40, /* 47 */ 40, 40, 40, 40, 40, 40, 40, 3, /* 47 */ 40, 60, 40, 40, 60, 60, 60, 60, /* 47 */ 60, 60, 60, 46, 46, 46, 46, 4, /* 47 */ 40, 40, 40, 40, 40, 40, 59, 60, /* 48 */ 60, 60, 60, 60, 60, 60, 60, 15, /* 48 */ 9, 9, 9, 9, 9, 9, 9, 9, /* 48 */ 9, 9, 3, 3, 46, 46, 46, 46, /* 48 */ 46, 46, 46, 46, 46, 46, 46, 46, /* 48 */ 46, 46, 46, 46, 46, 46, 46, 46, /* 48 */ 46, 46, 46, 46, 46, 46, 46, 46, /* 48 */ 46, 46, 46, 46, 46, 46, 46, 46, /* 48 */ 46, 40, 40, 46, 40, 46, 46, 40, /* 49 */ 40, 46, 40, 46, 46, 40, 46, 46, /* 49 */ 46, 46, 46, 46, 40, 40, 40, 40, /* 49 */ 46, 40, 40, 40, 40, 40, 40, 40, /* 49 */ 46, 40, 40, 40, 46, 40, 46, 40, /* 49 */ 46, 46, 40, 40, 46, 40, 40, 3, /* 49 */ 40, 60, 40, 40, 60, 60, 60, 60, /* 49 */ 60, 60, 46, 60, 60, 40, 46, 46, /* 49 */ 40, 40, 40, 40, 40, 46, 59, 46, /* 50 */ 60, 60, 60, 60, 60, 60, 46, 46, /* 50 */ 9, 9, 9, 9, 9, 9, 9, 9, /* 50 */ 9, 9, 46, 46, 40, 40, 46, 46, /* 50 */ 46, 46, 46, 46, 46, 46, 46, 46, /* 50 */ 46, 46, 46, 46, 46, 46, 46, 46, /* 50 */ 46, 46, 46, 46, 46, 46, 46, 46, /* 50 */ 46, 46, 46, 46, 46, 46, 46, 46, /* 50 */ 15, 15, 15, 15, 3, 3, 3, 3, /* 51 */ 3, 3, 3, 3, 3, 3, 3, 3, /* 51 */ 3, 3, 3, 15, 15, 15, 15, 15, /* 51 */ 60, 60, 15, 15, 15, 15, 15, 15, /* 51 */ 78, 78, 78, 78, 78, 78, 78, 78, /* 51 */ 78, 78, 85, 85, 85, 85, 85, 85, /* 51 */ 85, 85, 85, 85, 15, 60, 15, 60, /* 51 */ 15, 60, 5, 6, 5, 6, 80, 80, /* 51 */ 40, 40, 40, 40, 40, 40, 40, 40, /* 52 */ 46, 40, 40, 40, 40, 40, 40, 40, /* 52 */ 40, 40, 40, 40, 40, 40, 40, 40, /* 52 */ 40, 40, 40, 40, 40, 40, 40, 40, /* 52 */ 40, 40, 40, 40, 40, 40, 40, 40, /* 52 */ 40, 40, 46, 46, 46, 46, 46, 46, /* 52 */ 46, 60, 60, 60, 60, 60, 60, 60, /* 52 */ 60, 60, 60, 60, 60, 60, 60, 80, /* 52 */ 60, 60, 60, 60, 60, 3, 60, 60, /* 53 */ 60, 60, 60, 60, 46, 46, 46, 46, /* 53 */ 60, 60, 60, 60, 60, 60, 46, 60, /* 53 */ 46, 60, 60, 60, 60, 60, 60, 60, /* 53 */ 60, 60, 60, 60, 60, 60, 60, 60, /* 53 */ 60, 60, 60, 60, 60, 60, 46, 46, /* 53 */ 46, 60, 60, 60, 60, 60, 60, 60, /* 53 */ 46, 60, 46, 46, 46, 46, 46, 46, /* 53 */ 46, 46, 46, 46, 46, 46, 46, 46, /* 54 */ 46, 46, 46, 46, 46, 46, 46, 46, /* 54 */ 46, 46, 46, 46, 46, 46, 46, 46, /* 54 */ 46, 46, 46, 46, 46, 46, 46, 46, /* 54 */ 76, 76, 76, 76, 76, 76, 76, 76, /* 54 */ 76, 76, 76, 76, 76, 76, 76, 76, /* 54 */ 76, 76, 76, 76, 76, 76, 76, 76, /* 54 */ 76, 76, 76, 76, 76, 76, 76, 76, /* 54 */ 76, 76, 76, 76, 76, 76, 46, 46, /* 55 */ 46, 46, 46, 46, 46, 46, 46, 46, /* 55 */ 16, 16, 16, 16, 16, 16, 16, 16, /* 55 */ 16, 16, 16, 16, 16, 16, 16, 16, /* 55 */ 16, 16, 16, 16, 16, 16, 16, 16, /* 55 */ 16, 16, 16, 16, 16, 16, 16, 16, /* 55 */ 16, 16, 16, 16, 16, 16, 16, 46, /* 55 */ 46, 46, 46, 3, 46, 46, 46, 46, /* 55 */ 40, 40, 40, 40, 40, 40, 40, 40, /* 56 */ 40, 40, 40, 40, 40, 40, 40, 40, /* 56 */ 40, 40, 40, 40, 40, 40, 40, 40, /* 56 */ 40, 40, 40, 40, 40, 40, 40, 40, /* 56 */ 40, 40, 40, 40, 40, 40, 40, 40, /* 56 */ 40, 40, 40, 40, 40, 40, 40, 40, /* 56 */ 40, 40, 40, 40, 40, 40, 40, 40, /* 56 */ 40, 40, 40, 40, 40, 40, 40, 40, /* 56 */ 40, 40, 40, 40, 40, 40, 40, 40, /* 57 */ 40, 40, 40, 40, 40, 40, 40, 40, /* 57 */ 40, 40, 40, 40, 40, 40, 40, 40, /* 57 */ 40, 40, 46, 46, 46, 46, 46, 40, /* 57 */ 40, 40, 40, 40, 40, 40, 40, 40, /* 57 */ 40, 40, 40, 40, 40, 40, 40, 40, /* 57 */ 40, 40, 40, 40, 40, 40, 40, 40, /* 57 */ 40, 40, 40, 40, 40, 40, 40, 40, /* 57 */ 40, 40, 40, 40, 40, 40, 40, 40, /* 58 */ 40, 40, 40, 40, 40, 40, 40, 40, /* 58 */ 40, 40, 40, 40, 40, 40, 40, 40, /* 58 */ 40, 40, 40, 40, 40, 40, 40, 40, /* 58 */ 40, 40, 40, 46, 46, 46, 46, 46, /* 58 */ 40, 40, 40, 40, 40, 40, 40, 40, /* 58 */ 40, 40, 40, 40, 40, 40, 40, 40, /* 58 */ 40, 40, 40, 40, 40, 40, 40, 40, /* 58 */ 40, 40, 40, 40, 40, 40, 40, 40, /* 59 */ 40, 40, 40, 40, 40, 40, 40, 40, /* 59 */ 40, 40, 40, 40, 40, 40, 40, 40, /* 59 */ 40, 40, 40, 40, 40, 40, 40, 40, /* 59 */ 40, 40, 40, 40, 40, 40, 40, 40, /* 59 */ 40, 40, 40, 40, 40, 40, 40, 40, /* 59 */ 40, 40, 40, 40, 40, 40, 40, 40, /* 59 */ 40, 40, 46, 46, 46, 46, 46, 46, /* 59 */ 23, 24, 23, 24, 23, 24, 23, 24, /* 60 */ 23, 24, 23, 24, 23, 24, 23, 24, /* 60 */ 23, 24, 23, 24, 23, 24, 23, 24, /* 60 */ 23, 24, 23, 24, 23, 24, 23, 24, /* 60 */ 23, 24, 23, 24, 23, 24, 23, 24, /* 60 */ 23, 24, 23, 24, 23, 24, 23, 24, /* 60 */ 23, 24, 23, 24, 23, 24, 23, 24, /* 60 */ 23, 24, 23, 24, 23, 24, 23, 24, /* 60 */ 23, 24, 23, 24, 23, 24, 23, 24, /* 61 */ 23, 24, 23, 24, 23, 24, 23, 24, /* 61 */ 23, 24, 23, 24, 23, 24, 16, 16, /* 61 */ 16, 16, 16, 16, 46, 46, 46, 46, /* 61 */ 23, 24, 23, 24, 23, 24, 23, 24, /* 61 */ 23, 24, 23, 24, 23, 24, 23, 24, /* 61 */ 23, 24, 23, 24, 23, 24, 23, 24, /* 61 */ 23, 24, 23, 24, 23, 24, 23, 24, /* 61 */ 23, 24, 23, 24, 23, 24, 23, 24, /* 62 */ 23, 24, 23, 24, 23, 24, 23, 24, /* 62 */ 23, 24, 23, 24, 23, 24, 23, 24, /* 62 */ 23, 24, 23, 24, 23, 24, 23, 24, /* 62 */ 23, 24, 23, 24, 23, 24, 23, 24, /* 62 */ 23, 24, 23, 24, 23, 24, 23, 24, /* 62 */ 23, 24, 23, 24, 23, 24, 23, 24, /* 62 */ 23, 24, 46, 46, 46, 46, 46, 46, /* 62 */ 86, 86, 86, 86, 86, 86, 86, 86, /* 63 */ 87, 87, 87, 87, 87, 87, 87, 87, /* 63 */ 86, 86, 86, 86, 86, 86, 46, 46, /* 63 */ 87, 87, 87, 87, 87, 87, 46, 46, /* 63 */ 86, 86, 86, 86, 86, 86, 86, 86, /* 63 */ 87, 87, 87, 87, 87, 87, 87, 87, /* 63 */ 86, 86, 86, 86, 86, 86, 86, 86, /* 63 */ 87, 87, 87, 87, 87, 87, 87, 87, /* 63 */ 86, 86, 86, 86, 86, 86, 46, 46, /* 64 */ 87, 87, 87, 87, 87, 87, 46, 46, /* 64 */ 16, 86, 16, 86, 16, 86, 16, 86, /* 64 */ 46, 87, 46, 87, 46, 87, 46, 87, /* 64 */ 86, 86, 86, 86, 86, 86, 86, 86, /* 64 */ 87, 87, 87, 87, 87, 87, 87, 87, /* 64 */ 88, 88, 89, 89, 89, 89, 90, 90, /* 64 */ 91, 91, 92, 92, 93, 93, 46, 46, /* 64 */ 86, 86, 86, 86, 86, 86, 86, 86, /* 65 */ 87, 87, 87, 87, 87, 87, 87, 87, /* 65 */ 86, 86, 86, 86, 86, 86, 86, 86, /* 65 */ 87, 87, 87, 87, 87, 87, 87, 87, /* 65 */ 86, 86, 86, 86, 86, 86, 86, 86, /* 65 */ 87, 87, 87, 87, 87, 87, 87, 87, /* 65 */ 86, 86, 16, 94, 16, 46, 16, 16, /* 65 */ 87, 87, 95, 95, 96, 11, 38, 11, /* 65 */ 11, 11, 16, 94, 16, 46, 16, 16, /* 66 */ 97, 97, 97, 97, 96, 11, 11, 11, /* 66 */ 86, 86, 16, 16, 46, 46, 16, 16, /* 66 */ 87, 87, 98, 98, 46, 11, 11, 11, /* 66 */ 86, 86, 16, 16, 16, 99, 16, 16, /* 66 */ 87, 87, 100, 100, 101, 11, 11, 11, /* 66 */ 46, 46, 16, 94, 16, 46, 16, 16, /* 66 */ 102, 102, 103, 103, 96, 11, 11, 46, /* 66 */ 2, 2, 2, 2, 2, 2, 2, 2, /* 67 */ 2, 2, 2, 2, 104, 104, 104, 104, /* 67 */ 8, 8, 8, 8, 8, 8, 3, 3, /* 67 */ 5, 6, 5, 5, 5, 6, 5, 5, /* 67 */ 3, 3, 3, 3, 3, 3, 3, 3, /* 67 */ 105, 106, 104, 104, 104, 104, 104, 46, /* 67 */ 3, 3, 3, 3, 3, 3, 3, 3, /* 67 */ 3, 5, 6, 3, 3, 3, 3, 12, /* 67 */ 12, 3, 3, 3, 7, 5, 6, 46, /* 68 */ 46, 46, 46, 46, 46, 46, 46, 46, /* 68 */ 46, 46, 46, 46, 46, 46, 46, 46, /* 68 */ 46, 46, 46, 46, 46, 46, 46, 46, /* 68 */ 46, 46, 46, 46, 46, 46, 46, 46, /* 68 */ 46, 46, 104, 104, 104, 104, 104, 104, /* 68 */ 17, 46, 46, 46, 17, 17, 17, 17, /* 68 */ 17, 17, 7, 7, 7, 5, 6, 16, /* 68 */ 107, 107, 107, 107, 107, 107, 107, 107, /* 69 */ 107, 107, 7, 7, 7, 5, 6, 46, /* 69 */ 46, 46, 46, 46, 46, 46, 46, 46, /* 69 */ 46, 46, 46, 46, 46, 46, 46, 46, /* 69 */ 4, 4, 4, 4, 4, 4, 4, 4, /* 69 */ 4, 4, 4, 4, 46, 46, 46, 46, /* 69 */ 46, 46, 46, 46, 46, 46, 46, 46, /* 69 */ 46, 46, 46, 46, 46, 46, 46, 46, /* 69 */ 46, 46, 46, 46, 46, 46, 46, 46, /* 70 */ 46, 46, 46, 46, 46, 46, 46, 46, /* 70 */ 60, 60, 60, 60, 60, 60, 60, 60, /* 70 */ 60, 60, 60, 60, 60, 79, 79, 79, /* 70 */ 79, 60, 46, 46, 46, 46, 46, 46, /* 70 */ 46, 46, 46, 46, 46, 46, 46, 46, /* 70 */ 46, 46, 46, 46, 46, 46, 46, 46, /* 70 */ 46, 46, 46, 46, 46, 46, 46, 46, /* 70 */ 15, 15, 38, 15, 15, 15, 15, 38, /* 71 */ 15, 15, 16, 38, 38, 38, 16, 16, /* 71 */ 38, 38, 38, 16, 15, 38, 15, 15, /* 71 */ 38, 38, 38, 38, 38, 38, 15, 15, /* 71 */ 15, 15, 15, 15, 38, 15, 38, 15, /* 71 */ 38, 15, 38, 38, 38, 38, 16, 16, /* 71 */ 38, 38, 15, 38, 16, 40, 40, 40, /* 71 */ 40, 46, 46, 46, 46, 46, 46, 46, /* 71 */ 46, 46, 46, 46, 46, 46, 46, 46, /* 72 */ 46, 46, 46, 46, 46, 46, 46, 46, /* 72 */ 46, 46, 46, 19, 19, 19, 19, 19, /* 72 */ 19, 19, 19, 19, 19, 19, 19, 108, /* 72 */ 109, 109, 109, 109, 109, 109, 109, 109, /* 72 */ 109, 109, 109, 109, 110, 110, 110, 110, /* 72 */ 111, 111, 111, 111, 111, 111, 111, 111, /* 72 */ 111, 111, 111, 111, 112, 112, 112, 112, /* 72 */ 113, 113, 113, 46, 46, 46, 46, 46, /* 73 */ 46, 46, 46, 46, 46, 46, 46, 46, /* 73 */ 7, 7, 7, 7, 7, 15, 15, 15, /* 73 */ 15, 15, 15, 15, 15, 15, 15, 15, /* 73 */ 15, 15, 15, 15, 15, 15, 15, 15, /* 73 */ 15, 15, 15, 15, 15, 15, 15, 15, /* 73 */ 15, 15, 15, 15, 15, 15, 15, 15, /* 73 */ 15, 15, 15, 15, 15, 15, 15, 15, /* 73 */ 15, 15, 15, 15, 15, 15, 15, 15, /* 74 */ 15, 15, 15, 15, 15, 15, 15, 15, /* 74 */ 15, 15, 7, 15, 7, 15, 15, 15, /* 74 */ 15, 15, 15, 15, 15, 15, 15, 15, /* 74 */ 15, 15, 15, 15, 15, 15, 15, 15, /* 74 */ 15, 15, 15, 46, 46, 46, 46, 46, /* 74 */ 46, 46, 46, 46, 46, 46, 46, 46, /* 74 */ 46, 46, 46, 46, 46, 46, 46, 46, /* 74 */ 7, 7, 7, 7, 7, 7, 7, 7, /* 75 */ 7, 7, 7, 7, 7, 7, 7, 7, /* 75 */ 7, 7, 7, 7, 7, 7, 7, 7, /* 75 */ 7, 7, 7, 7, 7, 7, 7, 7, /* 75 */ 7, 7, 7, 7, 7, 7, 7, 7, /* 75 */ 7, 7, 7, 7, 7, 7, 7, 7, /* 75 */ 7, 7, 7, 7, 7, 7, 7, 7, /* 75 */ 7, 7, 7, 7, 7, 7, 7, 7, /* 75 */ 7, 7, 7, 7, 7, 7, 7, 7, /* 76 */ 7, 7, 7, 7, 7, 7, 7, 7, /* 76 */ 7, 7, 7, 7, 7, 7, 7, 7, /* 76 */ 7, 7, 7, 7, 7, 7, 7, 7, /* 76 */ 7, 7, 7, 7, 7, 7, 7, 7, /* 76 */ 7, 7, 7, 7, 7, 7, 7, 7, /* 76 */ 7, 7, 46, 46, 46, 46, 46, 46, /* 76 */ 46, 46, 46, 46, 46, 46, 46, 46, /* 76 */ 15, 46, 15, 15, 15, 15, 15, 15, /* 77 */ 7, 7, 7, 7, 15, 15, 15, 15, /* 77 */ 15, 15, 15, 15, 15, 15, 15, 15, /* 77 */ 15, 15, 15, 15, 15, 15, 15, 15, /* 77 */ 7, 7, 15, 15, 15, 15, 15, 15, /* 77 */ 15, 5, 6, 15, 15, 15, 15, 15, /* 77 */ 15, 15, 15, 15, 15, 15, 15, 15, /* 77 */ 15, 15, 15, 15, 15, 15, 15, 15, /* 77 */ 15, 15, 15, 15, 15, 15, 15, 15, /* 78 */ 15, 15, 15, 15, 15, 15, 15, 15, /* 78 */ 15, 15, 15, 15, 15, 15, 15, 15, /* 78 */ 15, 15, 15, 15, 15, 15, 15, 15, /* 78 */ 15, 15, 15, 15, 15, 15, 15, 15, /* 78 */ 15, 15, 15, 15, 15, 15, 15, 15, /* 78 */ 15, 15, 15, 15, 15, 15, 15, 15, /* 78 */ 15, 15, 15, 46, 46, 46, 46, 46, /* 78 */ 15, 15, 15, 15, 15, 15, 15, 15, /* 79 */ 15, 15, 15, 15, 15, 15, 15, 15, /* 79 */ 15, 15, 15, 15, 15, 15, 15, 15, /* 79 */ 15, 15, 15, 15, 15, 15, 15, 15, /* 79 */ 15, 15, 15, 15, 15, 46, 46, 46, /* 79 */ 46, 46, 46, 46, 46, 46, 46, 46, /* 79 */ 46, 46, 46, 46, 46, 46, 46, 46, /* 79 */ 46, 46, 46, 46, 46, 46, 46, 46, /* 79 */ 15, 15, 15, 15, 15, 15, 15, 15, /* 80 */ 15, 15, 15, 46, 46, 46, 46, 46, /* 80 */ 46, 46, 46, 46, 46, 46, 46, 46, /* 80 */ 46, 46, 46, 46, 46, 46, 46, 46, /* 80 */ 114, 114, 114, 114, 114, 114, 114, 114, /* 80 */ 114, 114, 114, 114, 114, 114, 114, 114, /* 80 */ 114, 114, 114, 114, 82, 82, 82, 82, /* 80 */ 82, 82, 82, 82, 82, 82, 82, 82, /* 80 */ 82, 82, 82, 82, 82, 82, 82, 82, /* 81 */ 115, 115, 115, 115, 115, 115, 115, 115, /* 81 */ 115, 115, 115, 115, 115, 115, 115, 115, /* 81 */ 115, 115, 115, 115, 15, 15, 15, 15, /* 81 */ 15, 15, 15, 15, 15, 15, 15, 15, /* 81 */ 15, 15, 15, 15, 15, 15, 15, 15, /* 81 */ 15, 15, 15, 15, 15, 15, 116, 116, /* 81 */ 116, 116, 116, 116, 116, 116, 116, 116, /* 81 */ 116, 116, 116, 116, 116, 116, 116, 116, /* 82 */ 116, 116, 116, 116, 116, 116, 116, 116, /* 82 */ 117, 117, 117, 117, 117, 117, 117, 117, /* 82 */ 117, 117, 117, 117, 117, 117, 117, 117, /* 82 */ 117, 117, 117, 117, 117, 117, 117, 117, /* 82 */ 117, 117, 118, 46, 46, 46, 46, 46, /* 82 */ 46, 46, 46, 46, 46, 46, 46, 46, /* 82 */ 46, 46, 46, 46, 46, 46, 46, 46, /* 82 */ 15, 15, 15, 15, 15, 15, 15, 15, /* 83 */ 15, 15, 15, 15, 15, 15, 15, 15, /* 83 */ 15, 15, 15, 15, 15, 15, 15, 15, /* 83 */ 15, 15, 15, 15, 15, 15, 15, 15, /* 83 */ 15, 15, 15, 15, 15, 15, 15, 15, /* 83 */ 15, 15, 15, 15, 15, 15, 15, 15, /* 83 */ 15, 15, 15, 15, 15, 15, 15, 15, /* 83 */ 15, 15, 15, 15, 15, 15, 15, 15, /* 83 */ 15, 15, 15, 15, 15, 15, 15, 15, /* 84 */ 15, 15, 15, 15, 15, 15, 15, 15, /* 84 */ 15, 15, 15, 15, 15, 15, 46, 46, /* 84 */ 46, 46, 46, 46, 46, 46, 46, 46, /* 84 */ 15, 15, 15, 15, 15, 15, 15, 15, /* 84 */ 15, 15, 15, 15, 15, 15, 15, 15, /* 84 */ 15, 15, 15, 15, 15, 15, 15, 15, /* 84 */ 15, 15, 15, 15, 15, 15, 15, 15, /* 84 */ 15, 15, 15, 15, 15, 15, 15, 15, /* 85 */ 15, 15, 15, 15, 15, 15, 15, 15, /* 85 */ 15, 15, 15, 15, 15, 15, 15, 15, /* 85 */ 15, 15, 15, 15, 15, 15, 15, 15, /* 85 */ 15, 15, 15, 15, 15, 15, 15, 15, /* 85 */ 15, 15, 15, 15, 15, 15, 15, 15, /* 85 */ 46, 46, 46, 46, 46, 46, 46, 46, /* 85 */ 46, 46, 46, 46, 46, 46, 46, 46, /* 85 */ 15, 15, 15, 15, 15, 15, 15, 15, /* 86 */ 15, 15, 15, 15, 15, 15, 15, 15, /* 86 */ 15, 15, 15, 15, 46, 46, 46, 46, /* 86 */ 46, 46, 15, 15, 15, 15, 15, 15, /* 86 */ 15, 15, 15, 15, 15, 15, 15, 15, /* 86 */ 15, 15, 15, 15, 15, 15, 15, 15, /* 86 */ 15, 15, 15, 15, 15, 15, 15, 15, /* 86 */ 15, 15, 15, 15, 15, 15, 15, 15, /* 86 */ 46, 15, 15, 15, 15, 46, 15, 15, /* 87 */ 15, 15, 46, 46, 15, 15, 15, 15, /* 87 */ 15, 15, 15, 15, 15, 15, 15, 15, /* 87 */ 15, 15, 15, 15, 15, 15, 15, 15, /* 87 */ 15, 15, 15, 15, 15, 15, 15, 15, /* 87 */ 46, 15, 15, 15, 15, 15, 15, 15, /* 87 */ 15, 15, 15, 15, 15, 15, 15, 15, /* 87 */ 15, 15, 15, 15, 15, 15, 15, 15, /* 87 */ 15, 15, 15, 15, 15, 15, 15, 15, /* 88 */ 15, 15, 15, 15, 46, 15, 46, 15, /* 88 */ 15, 15, 15, 46, 46, 46, 15, 46, /* 88 */ 15, 15, 15, 15, 15, 15, 15, 46, /* 88 */ 46, 15, 15, 15, 15, 15, 15, 15, /* 88 */ 46, 46, 46, 46, 46, 46, 46, 46, /* 88 */ 46, 46, 46, 46, 46, 46, 119, 119, /* 88 */ 119, 119, 119, 119, 119, 119, 119, 119, /* 88 */ 114, 114, 114, 114, 114, 114, 114, 114, /* 89 */ 114, 114, 83, 83, 83, 83, 83, 83, /* 89 */ 83, 83, 83, 83, 15, 46, 46, 46, /* 89 */ 15, 15, 15, 15, 15, 15, 15, 15, /* 89 */ 15, 15, 15, 15, 15, 15, 15, 15, /* 89 */ 15, 15, 15, 15, 15, 15, 15, 15, /* 89 */ 46, 15, 15, 15, 15, 15, 15, 15, /* 89 */ 15, 15, 15, 15, 15, 15, 15, 46, /* 89 */ 2, 3, 3, 3, 15, 59, 3, 120, /* 90 */ 5, 6, 5, 6, 5, 6, 5, 6, /* 90 */ 5, 6, 15, 15, 5, 6, 5, 6, /* 90 */ 5, 6, 5, 6, 8, 5, 6, 5, /* 90 */ 15, 121, 121, 121, 121, 121, 121, 121, /* 90 */ 121, 121, 60, 60, 60, 60, 60, 60, /* 90 */ 8, 59, 59, 59, 59, 59, 15, 15, /* 90 */ 46, 46, 46, 46, 46, 46, 46, 15, /* 90 */ 46, 40, 40, 40, 40, 40, 40, 40, /* 91 */ 40, 40, 40, 40, 40, 40, 40, 40, /* 91 */ 40, 40, 40, 40, 40, 40, 40, 40, /* 91 */ 40, 40, 40, 40, 40, 40, 40, 40, /* 91 */ 40, 40, 40, 40, 40, 40, 40, 40, /* 91 */ 40, 40, 40, 40, 40, 40, 40, 40, /* 91 */ 40, 40, 40, 40, 40, 40, 40, 40, /* 91 */ 40, 40, 40, 40, 40, 40, 40, 40, /* 91 */ 40, 40, 40, 40, 40, 40, 40, 40, /* 92 */ 40, 40, 40, 40, 40, 40, 40, 40, /* 92 */ 40, 40, 40, 40, 40, 46, 46, 46, /* 92 */ 46, 60, 60, 59, 59, 59, 59, 46, /* 92 */ 46, 40, 40, 40, 40, 40, 40, 40, /* 92 */ 40, 40, 40, 40, 40, 40, 40, 40, /* 92 */ 40, 40, 40, 40, 40, 40, 40, 40, /* 92 */ 40, 40, 40, 40, 40, 40, 40, 40, /* 92 */ 40, 40, 40, 40, 40, 40, 40, 40, /* 93 */ 40, 40, 40, 40, 40, 40, 40, 40, /* 93 */ 40, 40, 40, 40, 40, 40, 40, 40, /* 93 */ 40, 40, 40, 40, 40, 40, 40, 40, /* 93 */ 40, 40, 40, 40, 40, 40, 40, 40, /* 93 */ 40, 40, 40, 40, 40, 40, 40, 40, /* 93 */ 40, 40, 40, 40, 40, 40, 40, 40, /* 93 */ 40, 40, 40, 3, 59, 59, 59, 46, /* 93 */ 46, 46, 46, 46, 46, 40, 40, 40, /* 94 */ 40, 40, 40, 40, 40, 40, 40, 40, /* 94 */ 40, 40, 40, 40, 40, 40, 40, 40, /* 94 */ 40, 40, 40, 40, 40, 40, 40, 40, /* 94 */ 40, 40, 40, 40, 40, 40, 40, 40, /* 94 */ 40, 40, 40, 40, 40, 46, 46, 46, /* 94 */ 46, 40, 40, 40, 40, 40, 40, 40, /* 94 */ 40, 40, 40, 40, 40, 40, 40, 40, /* 94 */ 40, 40, 40, 40, 40, 40, 40, 40, /* 95 */ 40, 40, 40, 40, 40, 40, 40, 46, /* 95 */ 15, 15, 85, 85, 85, 85, 15, 15, /* 95 */ 15, 15, 15, 15, 15, 15, 15, 15, /* 95 */ 46, 46, 46, 46, 46, 46, 46, 46, /* 95 */ 46, 46, 46, 46, 46, 46, 46, 46, /* 95 */ 46, 46, 46, 46, 46, 46, 46, 46, /* 95 */ 46, 46, 46, 46, 46, 46, 46, 46, /* 95 */ 15, 15, 15, 15, 15, 15, 15, 15, /* 96 */ 15, 15, 15, 15, 15, 15, 15, 15, /* 96 */ 15, 15, 15, 15, 15, 15, 15, 15, /* 96 */ 15, 15, 15, 15, 15, 46, 46, 46, /* 96 */ 85, 85, 85, 85, 85, 85, 85, 85, /* 96 */ 85, 85, 15, 15, 15, 15, 15, 15, /* 96 */ 15, 15, 15, 15, 15, 15, 15, 15, /* 96 */ 15, 15, 15, 15, 15, 15, 15, 15, /* 96 */ 15, 15, 15, 15, 46, 46, 46, 46, /* 97 */ 46, 46, 46, 46, 46, 46, 46, 46, /* 97 */ 46, 46, 46, 46, 46, 46, 46, 46, /* 97 */ 46, 46, 46, 46, 46, 46, 46, 46, /* 97 */ 15, 15, 15, 15, 15, 15, 15, 15, /* 97 */ 15, 15, 15, 15, 15, 15, 15, 15, /* 97 */ 15, 15, 15, 15, 15, 15, 15, 15, /* 97 */ 15, 15, 15, 15, 46, 46, 46, 15, /* 97 */ 114, 114, 114, 114, 114, 114, 114, 114, /* 98 */ 114, 114, 15, 15, 15, 15, 15, 15, /* 98 */ 15, 15, 15, 15, 15, 15, 15, 15, /* 98 */ 15, 15, 15, 15, 15, 15, 15, 15, /* 98 */ 15, 15, 15, 15, 15, 15, 15, 15, /* 98 */ 15, 15, 15, 15, 15, 15, 15, 15, /* 98 */ 15, 46, 46, 46, 46, 46, 46, 46, /* 98 */ 46, 46, 46, 46, 46, 46, 46, 46, /* 98 */ 15, 15, 15, 15, 15, 15, 15, 15, /* 99 */ 15, 15, 15, 15, 46, 46, 46, 46, /* 99 */ 15, 15, 15, 15, 15, 15, 15, 15, /* 99 */ 15, 15, 15, 15, 15, 15, 15, 15, /* 99 */ 15, 15, 15, 15, 15, 15, 15, 15, /* 99 */ 15, 15, 15, 15, 15, 15, 15, 15, /* 99 */ 15, 15, 15, 15, 15, 15, 15, 15, /* 99 */ 15, 15, 15, 15, 15, 15, 15, 46, /* 99 */ 15, 15, 15, 15, 15, 15, 15, 15, /* 100 */ 15, 15, 15, 15, 15, 15, 15, 15, /* 100 */ 15, 15, 15, 15, 15, 15, 15, 15, /* 100 */ 15, 15, 15, 15, 15, 15, 15, 15, /* 100 */ 15, 15, 15, 15, 15, 15, 15, 15, /* 100 */ 15, 15, 15, 15, 15, 15, 15, 15, /* 100 */ 15, 15, 15, 15, 15, 15, 15, 46, /* 100 */ 46, 46, 46, 15, 15, 15, 15, 15, /* 100 */ 15, 15, 15, 15, 15, 15, 15, 15, /* 101 */ 15, 15, 15, 15, 15, 15, 15, 15, /* 101 */ 15, 15, 15, 15, 15, 15, 15, 15, /* 101 */ 15, 15, 15, 15, 15, 15, 46, 46, /* 101 */ 15, 15, 15, 15, 15, 15, 15, 15, /* 101 */ 15, 15, 15, 15, 15, 15, 15, 15, /* 101 */ 15, 15, 15, 15, 15, 15, 15, 15, /* 101 */ 15, 15, 15, 15, 15, 15, 15, 46, /* 101 */ 40, 40, 40, 40, 40, 40, 40, 40, /* 102 */ 40, 40, 40, 40, 40, 40, 40, 40, /* 102 */ 40, 40, 40, 40, 40, 40, 40, 40, /* 102 */ 40, 40, 40, 40, 40, 40, 40, 40, /* 102 */ 40, 40, 40, 40, 40, 40, 46, 46, /* 102 */ 46, 46, 46, 46, 46, 46, 46, 46, /* 102 */ 46, 46, 46, 46, 46, 46, 46, 46, /* 102 */ 46, 46, 46, 46, 46, 46, 46, 46, /* 102 */ 40, 40, 40, 40, 40, 40, 40, 40, /* 103 */ 40, 40, 40, 40, 40, 40, 40, 40, /* 103 */ 40, 40, 40, 40, 40, 40, 40, 40, /* 103 */ 40, 40, 40, 40, 40, 40, 40, 40, /* 103 */ 40, 40, 40, 40, 46, 46, 46, 46, /* 103 */ 46, 46, 46, 46, 46, 46, 46, 46, /* 103 */ 46, 46, 46, 46, 46, 46, 46, 46, /* 103 */ 46, 46, 46, 46, 46, 46, 46, 46, /* 103 */ 122, 122, 122, 122, 122, 122, 122, 122, /* 104 */ 122, 122, 122, 122, 122, 122, 122, 122, /* 104 */ 122, 122, 122, 122, 122, 122, 122, 122, /* 104 */ 122, 122, 122, 122, 122, 122, 122, 122, /* 104 */ 122, 122, 122, 122, 122, 122, 122, 122, /* 104 */ 122, 122, 122, 122, 122, 122, 122, 122, /* 104 */ 122, 122, 122, 122, 122, 122, 122, 122, /* 104 */ 122, 122, 122, 122, 122, 122, 122, 122, /* 104 */ 123, 123, 123, 123, 123, 123, 123, 123, /* 105 */ 123, 123, 123, 123, 123, 123, 123, 123, /* 105 */ 123, 123, 123, 123, 123, 123, 123, 123, /* 105 */ 123, 123, 123, 123, 123, 123, 123, 123, /* 105 */ 123, 123, 123, 123, 123, 123, 123, 123, /* 105 */ 123, 123, 123, 123, 123, 123, 123, 123, /* 105 */ 123, 123, 123, 123, 123, 123, 123, 123, /* 105 */ 123, 123, 123, 123, 123, 123, 123, 123, /* 105 */ 40, 40, 40, 40, 40, 40, 40, 40, /* 106 */ 40, 40, 40, 40, 40, 40, 40, 40, /* 106 */ 40, 40, 40, 40, 40, 40, 40, 40, /* 106 */ 40, 40, 40, 40, 40, 40, 40, 40, /* 106 */ 40, 40, 40, 40, 40, 40, 40, 40, /* 106 */ 40, 40, 40, 40, 40, 40, 46, 46, /* 106 */ 46, 46, 46, 46, 46, 46, 46, 46, /* 106 */ 46, 46, 46, 46, 46, 46, 46, 46, /* 106 */ 16, 16, 16, 16, 16, 16, 16, 46, /* 107 */ 46, 46, 46, 46, 46, 46, 46, 46, /* 107 */ 46, 46, 46, 16, 16, 16, 16, 16, /* 107 */ 46, 46, 46, 46, 46, 46, 60, 40, /* 107 */ 40, 40, 40, 40, 40, 40, 40, 40, /* 107 */ 40, 7, 40, 40, 40, 40, 40, 40, /* 107 */ 40, 40, 40, 40, 40, 40, 40, 46, /* 107 */ 40, 40, 40, 40, 40, 46, 40, 46, /* 107 */ 40, 40, 46, 40, 40, 46, 40, 40, /* 108 */ 40, 40, 40, 40, 40, 40, 40, 40, /* 108 */ 40, 40, 40, 40, 40, 40, 40, 40, /* 108 */ 40, 40, 40, 40, 40, 40, 40, 40, /* 108 */ 40, 40, 40, 40, 40, 40, 40, 40, /* 108 */ 40, 40, 40, 40, 40, 40, 40, 40, /* 108 */ 40, 40, 40, 40, 40, 40, 40, 40, /* 108 */ 40, 40, 40, 40, 40, 40, 40, 40, /* 108 */ 40, 40, 40, 40, 40, 40, 40, 40, /* 109 */ 40, 40, 40, 40, 40, 40, 40, 40, /* 109 */ 40, 40, 40, 40, 40, 40, 40, 40, /* 109 */ 40, 40, 40, 40, 40, 40, 40, 40, /* 109 */ 40, 40, 40, 40, 40, 40, 40, 40, /* 109 */ 40, 40, 40, 40, 40, 40, 40, 40, /* 109 */ 40, 40, 46, 46, 46, 46, 46, 46, /* 109 */ 46, 46, 46, 46, 46, 46, 46, 46, /* 109 */ 46, 46, 46, 46, 46, 46, 46, 46, /* 110 */ 46, 46, 46, 46, 46, 46, 46, 46, /* 110 */ 46, 46, 46, 40, 40, 40, 40, 40, /* 110 */ 40, 40, 40, 40, 40, 40, 40, 40, /* 110 */ 40, 40, 40, 40, 40, 40, 40, 40, /* 110 */ 40, 40, 40, 40, 40, 40, 40, 40, /* 110 */ 40, 40, 40, 40, 40, 40, 40, 40, /* 110 */ 40, 40, 40, 40, 40, 40, 40, 40, /* 110 */ 40, 40, 40, 40, 40, 40, 40, 40, /* 111 */ 40, 40, 40, 40, 40, 40, 40, 40, /* 111 */ 40, 40, 40, 40, 40, 40, 40, 40, /* 111 */ 40, 40, 40, 40, 40, 40, 40, 40, /* 111 */ 40, 40, 40, 40, 40, 40, 40, 40, /* 111 */ 40, 40, 40, 40, 40, 40, 40, 40, /* 111 */ 40, 40, 40, 40, 40, 40, 40, 40, /* 111 */ 40, 40, 40, 40, 40, 40, 5, 6, /* 111 */ 46, 46, 46, 46, 46, 46, 46, 46, /* 112 */ 46, 46, 46, 46, 46, 46, 46, 46, /* 112 */ 40, 40, 40, 40, 40, 40, 40, 40, /* 112 */ 40, 40, 40, 40, 40, 40, 40, 40, /* 112 */ 40, 40, 40, 40, 40, 40, 40, 40, /* 112 */ 40, 40, 40, 40, 40, 40, 40, 40, /* 112 */ 40, 40, 40, 40, 40, 40, 40, 40, /* 112 */ 40, 40, 40, 40, 40, 40, 40, 40, /* 112 */ 40, 40, 40, 40, 40, 40, 40, 40, /* 113 */ 40, 40, 40, 40, 40, 40, 40, 40, /* 113 */ 46, 46, 40, 40, 40, 40, 40, 40, /* 113 */ 40, 40, 40, 40, 40, 40, 40, 40, /* 113 */ 40, 40, 40, 40, 40, 40, 40, 40, /* 113 */ 40, 40, 40, 40, 40, 40, 40, 40, /* 113 */ 40, 40, 40, 40, 40, 40, 40, 40, /* 113 */ 40, 40, 40, 40, 40, 40, 40, 40, /* 113 */ 40, 40, 40, 40, 40, 40, 40, 40, /* 114 */ 46, 46, 46, 46, 46, 46, 46, 46, /* 114 */ 46, 46, 46, 46, 46, 46, 46, 46, /* 114 */ 46, 46, 46, 46, 46, 46, 46, 46, /* 114 */ 46, 46, 46, 46, 46, 46, 46, 46, /* 114 */ 46, 46, 46, 46, 46, 46, 46, 46, /* 114 */ 40, 40, 40, 40, 40, 40, 40, 40, /* 114 */ 40, 40, 40, 40, 46, 46, 46, 46, /* 114 */ 46, 46, 46, 46, 46, 46, 46, 46, /* 115 */ 46, 46, 46, 46, 46, 46, 46, 46, /* 115 */ 46, 46, 46, 46, 46, 46, 46, 46, /* 115 */ 46, 46, 46, 46, 46, 46, 46, 46, /* 115 */ 60, 60, 60, 60, 46, 46, 46, 46, /* 115 */ 46, 46, 46, 46, 46, 46, 46, 46, /* 115 */ 3, 8, 8, 12, 12, 5, 6, 5, /* 115 */ 6, 5, 6, 5, 6, 5, 6, 5, /* 115 */ 6, 5, 6, 5, 6, 46, 46, 46, /* 116 */ 46, 3, 3, 3, 3, 12, 12, 12, /* 116 */ 3, 3, 3, 46, 3, 3, 3, 3, /* 116 */ 8, 5, 6, 5, 6, 5, 6, 3, /* 116 */ 3, 3, 7, 8, 7, 7, 7, 46, /* 116 */ 3, 4, 3, 3, 46, 46, 46, 46, /* 116 */ 40, 40, 40, 46, 40, 46, 40, 40, /* 116 */ 40, 40, 40, 40, 40, 40, 40, 40, /* 116 */ 40, 40, 40, 40, 40, 40, 40, 40, /* 117 */ 40, 40, 40, 40, 40, 40, 40, 40, /* 117 */ 40, 40, 40, 40, 40, 40, 40, 40, /* 117 */ 40, 40, 40, 40, 40, 40, 40, 40, /* 117 */ 40, 40, 40, 40, 40, 40, 40, 40, /* 117 */ 40, 40, 40, 40, 40, 40, 40, 40, /* 117 */ 40, 40, 40, 40, 40, 40, 40, 40, /* 117 */ 40, 40, 40, 40, 40, 46, 46, 104, /* 117 */ 46, 3, 3, 3, 4, 3, 3, 3, /* 118 */ 5, 6, 3, 7, 3, 8, 3, 3, /* 118 */ 9, 9, 9, 9, 9, 9, 9, 9, /* 118 */ 9, 9, 3, 3, 7, 7, 7, 3, /* 118 */ 3, 10, 10, 10, 10, 10, 10, 10, /* 118 */ 10, 10, 10, 10, 10, 10, 10, 10, /* 118 */ 10, 10, 10, 10, 10, 10, 10, 10, /* 118 */ 10, 10, 10, 5, 3, 6, 11, 12, /* 118 */ 11, 13, 13, 13, 13, 13, 13, 13, /* 119 */ 13, 13, 13, 13, 13, 13, 13, 13, /* 119 */ 13, 13, 13, 13, 13, 13, 13, 13, /* 119 */ 13, 13, 13, 5, 7, 6, 7, 46, /* 119 */ 46, 3, 5, 6, 3, 3, 40, 40, /* 119 */ 40, 40, 40, 40, 40, 40, 40, 40, /* 119 */ 59, 40, 40, 40, 40, 40, 40, 40, /* 119 */ 40, 40, 40, 40, 40, 40, 40, 40, /* 119 */ 40, 40, 40, 40, 40, 40, 40, 40, /* 120 */ 40, 40, 40, 40, 40, 40, 40, 40, /* 120 */ 40, 40, 40, 40, 40, 40, 40, 40, /* 120 */ 40, 40, 40, 40, 40, 40, 59, 59, /* 120 */ 40, 40, 40, 40, 40, 40, 40, 40, /* 120 */ 40, 40, 40, 40, 40, 40, 40, 40, /* 120 */ 40, 40, 40, 40, 40, 40, 40, 40, /* 120 */ 40, 40, 40, 40, 40, 40, 40, 46, /* 120 */ 46, 46, 40, 40, 40, 40, 40, 40, /* 121 */ 46, 46, 40, 40, 40, 40, 40, 40, /* 121 */ 46, 46, 40, 40, 40, 40, 40, 40, /* 121 */ 46, 46, 40, 40, 40, 46, 46, 46, /* 121 */ 4, 4, 7, 11, 15, 4, 4, 46, /* 121 */ 7, 7, 7, 7, 7, 15, 15, 46, /* 121 */ 46, 46, 46, 46, 46, 46, 46, 46, /* 121 */ 46, 46, 46, 46, 46, 15, 46, 46 /* 121 */ }; /* The A table has 124 entries for a total of 496 bytes. */ const uint32 js_A[] = { 0x0001000F, /* 0 Cc, ignorable */ 0x0004000F, /* 1 Cc, whitespace */ 0x0004000C, /* 2 Zs, whitespace */ 0x00000018, /* 3 Po */ 0x0006001A, /* 4 Sc, currency */ 0x00000015, /* 5 Ps */ 0x00000016, /* 6 Pe */ 0x00000019, /* 7 Sm */ 0x00000014, /* 8 Pd */ 0x00036089, /* 9 Nd, identifier part, decimal 16 */ 0x0827FF81, /* 10 Lu, hasLower (add 32), identifier start, supradecimal 31 */ 0x0000001B, /* 11 Sk */ 0x00050017, /* 12 Pc, underscore */ 0x0817FF82, /* 13 Ll, hasUpper (subtract 32), identifier start, supradecimal 31 */ 0x0000000C, /* 14 Zs */ 0x0000001C, /* 15 So */ 0x00070182, /* 16 Ll, identifier start */ 0x0000600B, /* 17 No, decimal 16 */ 0x0000500B, /* 18 No, decimal 8 */ 0x0000800B, /* 19 No, strange */ 0x08270181, /* 20 Lu, hasLower (add 32), identifier start */ 0x08170182, /* 21 Ll, hasUpper (subtract 32), identifier start */ 0xE1D70182, /* 22 Ll, hasUpper (subtract -121), identifier start */ 0x00670181, /* 23 Lu, hasLower (add 1), identifier start */ 0x00570182, /* 24 Ll, hasUpper (subtract 1), identifier start */ 0xCE670181, /* 25 Lu, hasLower (add -199), identifier start */ 0x3A170182, /* 26 Ll, hasUpper (subtract 232), identifier start */ 0xE1E70181, /* 27 Lu, hasLower (add -121), identifier start */ 0x4B170182, /* 28 Ll, hasUpper (subtract 300), identifier start */ 0x34A70181, /* 29 Lu, hasLower (add 210), identifier start */ 0x33A70181, /* 30 Lu, hasLower (add 206), identifier start */ 0x33670181, /* 31 Lu, hasLower (add 205), identifier start */ 0x32A70181, /* 32 Lu, hasLower (add 202), identifier start */ 0x32E70181, /* 33 Lu, hasLower (add 203), identifier start */ 0x33E70181, /* 34 Lu, hasLower (add 207), identifier start */ 0x34E70181, /* 35 Lu, hasLower (add 211), identifier start */ 0x34670181, /* 36 Lu, hasLower (add 209), identifier start */ 0x35670181, /* 37 Lu, hasLower (add 213), identifier start */ 0x00070181, /* 38 Lu, identifier start */ 0x36A70181, /* 39 Lu, hasLower (add 218), identifier start */ 0x00070185, /* 40 Lo, identifier start */ 0x36670181, /* 41 Lu, hasLower (add 217), identifier start */ 0x36E70181, /* 42 Lu, hasLower (add 219), identifier start */ 0x00AF0181, /* 43 Lu, hasLower (add 2), hasTitle, identifier start */ 0x007F0183, /* 44 Lt, hasUpper (subtract 1), hasLower (add 1), hasTitle, identifier start */ 0x009F0182, /* 45 Ll, hasUpper (subtract 2), hasTitle, identifier start */ 0x00000000, /* 46 unassigned */ 0x34970182, /* 47 Ll, hasUpper (subtract 210), identifier start */ 0x33970182, /* 48 Ll, hasUpper (subtract 206), identifier start */ 0x33570182, /* 49 Ll, hasUpper (subtract 205), identifier start */ 0x32970182, /* 50 Ll, hasUpper (subtract 202), identifier start */ 0x32D70182, /* 51 Ll, hasUpper (subtract 203), identifier start */ 0x33D70182, /* 52 Ll, hasUpper (subtract 207), identifier start */ 0x34570182, /* 53 Ll, hasUpper (subtract 209), identifier start */ 0x34D70182, /* 54 Ll, hasUpper (subtract 211), identifier start */ 0x35570182, /* 55 Ll, hasUpper (subtract 213), identifier start */ 0x36970182, /* 56 Ll, hasUpper (subtract 218), identifier start */ 0x36570182, /* 57 Ll, hasUpper (subtract 217), identifier start */ 0x36D70182, /* 58 Ll, hasUpper (subtract 219), identifier start */ 0x00070084, /* 59 Lm, identifier start */ 0x00030086, /* 60 Mn, identifier part */ 0x09A70181, /* 61 Lu, hasLower (add 38), identifier start */ 0x09670181, /* 62 Lu, hasLower (add 37), identifier start */ 0x10270181, /* 63 Lu, hasLower (add 64), identifier start */ 0x0FE70181, /* 64 Lu, hasLower (add 63), identifier start */ 0x09970182, /* 65 Ll, hasUpper (subtract 38), identifier start */ 0x09570182, /* 66 Ll, hasUpper (subtract 37), identifier start */ 0x10170182, /* 67 Ll, hasUpper (subtract 64), identifier start */ 0x0FD70182, /* 68 Ll, hasUpper (subtract 63), identifier start */ 0x0F970182, /* 69 Ll, hasUpper (subtract 62), identifier start */ 0x0E570182, /* 70 Ll, hasUpper (subtract 57), identifier start */ 0x0BD70182, /* 71 Ll, hasUpper (subtract 47), identifier start */ 0x0D970182, /* 72 Ll, hasUpper (subtract 54), identifier start */ 0x15970182, /* 73 Ll, hasUpper (subtract 86), identifier start */ 0x14170182, /* 74 Ll, hasUpper (subtract 80), identifier start */ 0x14270181, /* 75 Lu, hasLower (add 80), identifier start */ 0x0C270181, /* 76 Lu, hasLower (add 48), identifier start */ 0x0C170182, /* 77 Ll, hasUpper (subtract 48), identifier start */ 0x00034089, /* 78 Nd, identifier part, decimal 0 */ 0x00000087, /* 79 Me */ 0x00030088, /* 80 Mc, identifier part */ 0x00037489, /* 81 Nd, identifier part, decimal 26 */ 0x00005A0B, /* 82 No, decimal 13 */ 0x00006E0B, /* 83 No, decimal 23 */ 0x0000740B, /* 84 No, decimal 26 */ 0x0000000B, /* 85 No */ 0xFE170182, /* 86 Ll, hasUpper (subtract -8), identifier start */ 0xFE270181, /* 87 Lu, hasLower (add -8), identifier start */ 0xED970182, /* 88 Ll, hasUpper (subtract -74), identifier start */ 0xEA970182, /* 89 Ll, hasUpper (subtract -86), identifier start */ 0xE7170182, /* 90 Ll, hasUpper (subtract -100), identifier start */ 0xE0170182, /* 91 Ll, hasUpper (subtract -128), identifier start */ 0xE4170182, /* 92 Ll, hasUpper (subtract -112), identifier start */ 0xE0970182, /* 93 Ll, hasUpper (subtract -126), identifier start */ 0xFDD70182, /* 94 Ll, hasUpper (subtract -9), identifier start */ 0xEDA70181, /* 95 Lu, hasLower (add -74), identifier start */ 0xFDE70181, /* 96 Lu, hasLower (add -9), identifier start */ 0xEAA70181, /* 97 Lu, hasLower (add -86), identifier start */ 0xE7270181, /* 98 Lu, hasLower (add -100), identifier start */ 0xFE570182, /* 99 Ll, hasUpper (subtract -7), identifier start */ 0xE4270181, /* 100 Lu, hasLower (add -112), identifier start */ 0xFE670181, /* 101 Lu, hasLower (add -7), identifier start */ 0xE0270181, /* 102 Lu, hasLower (add -128), identifier start */ 0xE0A70181, /* 103 Lu, hasLower (add -126), identifier start */ 0x00010010, /* 104 Cf, ignorable */ 0x0004000D, /* 105 Zl, whitespace */ 0x0004000E, /* 106 Zp, whitespace */ 0x0000400B, /* 107 No, decimal 0 */ 0x0000440B, /* 108 No, decimal 2 */ 0x0427438A, /* 109 Nl, hasLower (add 16), identifier start, decimal 1 */ 0x0427818A, /* 110 Nl, hasLower (add 16), identifier start, strange */ 0x0417638A, /* 111 Nl, hasUpper (subtract 16), identifier start, decimal 17 */ 0x0417818A, /* 112 Nl, hasUpper (subtract 16), identifier start, strange */ 0x0007818A, /* 113 Nl, identifier start, strange */ 0x0000420B, /* 114 No, decimal 1 */ 0x0000720B, /* 115 No, decimal 25 */ 0x06A0001C, /* 116 So, hasLower (add 26) */ 0x0690001C, /* 117 So, hasUpper (subtract 26) */ 0x00006C0B, /* 118 No, decimal 22 */ 0x0000560B, /* 119 No, decimal 11 */ 0x0007738A, /* 120 Nl, identifier start, decimal 25 */ 0x0007418A, /* 121 Nl, identifier start, decimal 0 */ 0x00000013, /* 122 Cs */ 0x00000012 /* 123 Co */ }; const jschar js_uriReservedPlusPound_ucstr[] = {';', '/', '?', ':', '@', '&', '=', '+', '$', ',', '#', 0}; const jschar js_uriUnescaped_ucstr[] = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', '-', '_', '.', '!', '~', '*', '\'', '(', ')', 0}; #define URI_CHUNK 64U /* Concatenate jschars onto an unshared/newborn JSString. */ static JSBool AddCharsToURI(JSContext *cx, JSString *str, const jschar *chars, size_t length) { size_t total; JS_ASSERT(!JSSTRING_IS_DEPENDENT(str)); total = str->length + length + 1; if (!str->chars || JS_HOWMANY(total, URI_CHUNK) > JS_HOWMANY(str->length + 1, URI_CHUNK)) { total = JS_ROUNDUP(total, URI_CHUNK); str->chars = JS_realloc(cx, str->chars, total * sizeof(jschar)); if (!str->chars) return JS_FALSE; } js_strncpy(str->chars + str->length, chars, length); str->length += length; str->chars[str->length] = 0; return JS_TRUE; } /* * ECMA 3, 15.1.3 URI Handling Function Properties * * The following are implementations of the algorithms * given in the ECMA specification for the hidden functions * 'Encode' and 'Decode'. */ static JSBool Encode(JSContext *cx, JSString *str, const jschar *unescapedSet, const jschar *unescapedSet2, jsval *rval) { size_t length, j, k, L; jschar *chars, c, c2; uint32 v; uint8 utf8buf[6]; jschar hexBuf[4]; static const char HexDigits[] = "0123456789ABCDEF"; /* NB: uppercase */ JSString *R; length = JSSTRING_LENGTH(str); if (length == 0) { *rval = STRING_TO_JSVAL(cx->runtime->emptyString); return JS_TRUE; } R = js_NewString(cx, NULL, 0, 0); if (!R) return JS_FALSE; hexBuf[0] = '%'; hexBuf[3] = 0; chars = JSSTRING_CHARS(str); for (k = 0; k < length; k++) { c = chars[k]; if (js_strchr(unescapedSet, c) || (unescapedSet2 && js_strchr(unescapedSet2, c))) { if (!AddCharsToURI(cx, R, &c, 1)) return JS_FALSE; } else { if ((c >= 0xDC00) && (c <= 0xDFFF)) { JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_BAD_URI, NULL); return JS_FALSE; } if (c < 0xD800 || c > 0xDBFF) { v = c; } else { k++; if (k == length) { JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_BAD_URI, NULL); return JS_FALSE; } c2 = chars[k]; if ((c2 < 0xDC00) || (c2 > 0xDFFF)) { JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_BAD_URI, NULL); return JS_FALSE; } v = ((c - 0xD800) << 10) + (c2 - 0xDC00) + 0x10000; } L = js_OneUcs4ToUtf8Char(utf8buf, v); for (j = 0; j < L; j++) { hexBuf[1] = HexDigits[utf8buf[j] >> 4]; hexBuf[2] = HexDigits[utf8buf[j] & 0xf]; if (!AddCharsToURI(cx, R, hexBuf, 3)) return JS_FALSE; } } } /* * Shrinking realloc can fail (e.g., with a BSD-style allocator), but we * don't worry about that case here. Worst case, R hangs onto URI_CHUNK-1 * more jschars than it needs. */ chars = (jschar *) JS_realloc(cx, R->chars, (R->length+1) * sizeof(jschar)); if (chars) R->chars = chars; *rval = STRING_TO_JSVAL(R); return JS_TRUE; } static JSBool Decode(JSContext *cx, JSString *str, const jschar *reservedSet, jsval *rval) { size_t length, start, k; jschar *chars, c, H; uint32 v; jsuint B; uint8 octets[6]; JSString *R; intN j, n; length = JSSTRING_LENGTH(str); if (length == 0) { *rval = STRING_TO_JSVAL(cx->runtime->emptyString); return JS_TRUE; } R = js_NewString(cx, NULL, 0, 0); if (!R) return JS_FALSE; chars = JSSTRING_CHARS(str); for (k = 0; k < length; k++) { c = chars[k]; if (c == '%') { start = k; if ((k + 2) >= length) goto bad; if (!JS7_ISHEX(chars[k+1]) || !JS7_ISHEX(chars[k+2])) goto bad; B = JS7_UNHEX(chars[k+1]) * 16 + JS7_UNHEX(chars[k+2]); k += 2; if (!(B & 0x80)) { c = (jschar)B; } else { n = 1; while (B & (0x80 >> n)) n++; if (n == 1 || n > 6) goto bad; octets[0] = (uint8)B; if (k + 3 * (n - 1) >= length) goto bad; for (j = 1; j < n; j++) { k++; if (chars[k] != '%') goto bad; if (!JS7_ISHEX(chars[k+1]) || !JS7_ISHEX(chars[k+2])) goto bad; B = JS7_UNHEX(chars[k+1]) * 16 + JS7_UNHEX(chars[k+2]); if ((B & 0xC0) != 0x80) goto bad; k += 2; octets[j] = (char)B; } v = Utf8ToOneUcs4Char(octets, n); if (v >= 0x10000) { v -= 0x10000; if (v > 0xFFFFF) goto bad; c = (jschar)((v & 0x3FF) + 0xDC00); H = (jschar)((v >> 10) + 0xD800); if (!AddCharsToURI(cx, R, &H, 1)) return JS_FALSE; } else { c = (jschar)v; } } if (js_strchr(reservedSet, c)) { if (!AddCharsToURI(cx, R, &chars[start], (k - start + 1))) return JS_FALSE; } else { if (!AddCharsToURI(cx, R, &c, 1)) return JS_FALSE; } } else { if (!AddCharsToURI(cx, R, &c, 1)) return JS_FALSE; } } /* * Shrinking realloc can fail (e.g., with a BSD-style allocator), but we * don't worry about that case here. Worst case, R hangs onto URI_CHUNK-1 * more jschars than it needs. */ chars = (jschar *) JS_realloc(cx, R->chars, (R->length+1) * sizeof(jschar)); if (chars) R->chars = chars; *rval = STRING_TO_JSVAL(R); return JS_TRUE; bad: JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_BAD_URI); return JS_FALSE; } static JSBool str_decodeURI(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) { JSString *str; str = js_ValueToString(cx, argv[0]); if (!str) return JS_FALSE; argv[0] = STRING_TO_JSVAL(str); return Decode(cx, str, js_uriReservedPlusPound_ucstr, rval); } static JSBool str_decodeURI_Component(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) { JSString *str; str = js_ValueToString(cx, argv[0]); if (!str) return JS_FALSE; argv[0] = STRING_TO_JSVAL(str); return Decode(cx, str, js_empty_ucstr, rval); } static JSBool str_encodeURI(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) { JSString *str; str = js_ValueToString(cx, argv[0]); if (!str) return JS_FALSE; argv[0] = STRING_TO_JSVAL(str); return Encode(cx, str, js_uriReservedPlusPound_ucstr, js_uriUnescaped_ucstr, rval); } static JSBool str_encodeURI_Component(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) { JSString *str; str = js_ValueToString(cx, argv[0]); if (!str) return JS_FALSE; argv[0] = STRING_TO_JSVAL(str); return Encode(cx, str, js_uriUnescaped_ucstr, NULL, rval); } /* * Convert one UCS-4 char and write it into a UTF-8 buffer, which must be at * least 6 bytes long. Return the number of UTF-8 bytes of data written. */ int js_OneUcs4ToUtf8Char(uint8 *utf8Buffer, uint32 ucs4Char) { int utf8Length = 1; JS_ASSERT(ucs4Char <= 0x7FFFFFFF); if (ucs4Char < 0x80) { *utf8Buffer = (uint8)ucs4Char; } else { int i; uint32 a = ucs4Char >> 11; utf8Length = 2; while (a) { a >>= 5; utf8Length++; } i = utf8Length; while (--i) { utf8Buffer[i] = (uint8)((ucs4Char & 0x3F) | 0x80); ucs4Char >>= 6; } *utf8Buffer = (uint8)(0x100 - (1 << (8-utf8Length)) + ucs4Char); } return utf8Length; } /* * Convert a utf8 character sequence into a UCS-4 character and return that * character. It is assumed that the caller already checked that the sequence * is valid. */ static uint32 Utf8ToOneUcs4Char(const uint8 *utf8Buffer, int utf8Length) { uint32 ucs4Char; uint32 minucs4Char; /* from Unicode 3.1, non-shortest form is illegal */ static const uint32 minucs4Table[] = { 0x00000080, 0x00000800, 0x0001000, 0x0020000, 0x0400000 }; JS_ASSERT(utf8Length >= 1 && utf8Length <= 6); if (utf8Length == 1) { ucs4Char = *utf8Buffer; JS_ASSERT(!(ucs4Char & 0x80)); } else { JS_ASSERT((*utf8Buffer & (0x100 - (1 << (7-utf8Length)))) == (0x100 - (1 << (8-utf8Length)))); ucs4Char = *utf8Buffer++ & ((1<<(7-utf8Length))-1); minucs4Char = minucs4Table[utf8Length-2]; while (--utf8Length) { JS_ASSERT((*utf8Buffer & 0xC0) == 0x80); ucs4Char = ucs4Char<<6 | (*utf8Buffer++ & 0x3F); } if (ucs4Char < minucs4Char || ucs4Char == 0xFFFE || ucs4Char == 0xFFFF) { ucs4Char = 0xFFFD; } } return ucs4Char; } pacparser-1.4.5/src/spidermonkey/js/src/jsstr.h000066400000000000000000000473341464010763600215440ustar00rootroot00000000000000/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- * * ***** BEGIN LICENSE BLOCK ***** * Version: MPL 1.1/GPL 2.0/LGPL 2.1 * * The contents of this file are subject to the Mozilla Public License Version * 1.1 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * http://www.mozilla.org/MPL/ * * Software distributed under the License is distributed on an "AS IS" basis, * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License * for the specific language governing rights and limitations under the * License. * * The Original Code is Mozilla Communicator client code, released * March 31, 1998. * * The Initial Developer of the Original Code is * Netscape Communications Corporation. * Portions created by the Initial Developer are Copyright (C) 1998 * the Initial Developer. All Rights Reserved. * * Contributor(s): * * Alternatively, the contents of this file may be used under the terms of * either of the GNU General Public License Version 2 or later (the "GPL"), * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), * in which case the provisions of the GPL or the LGPL are applicable instead * of those above. If you wish to allow use of your version of this file only * under the terms of either the GPL or the LGPL, and not to allow others to * use your version of this file under the terms of the MPL, indicate your * decision by deleting the provisions above and replace them with the notice * and other provisions required by the GPL or the LGPL. If you do not delete * the provisions above, a recipient may use your version of this file under * the terms of any one of the MPL, the GPL or the LGPL. * * ***** END LICENSE BLOCK ***** */ #ifndef jsstr_h___ #define jsstr_h___ /* * JS string type implementation. * * A JS string is a counted array of unicode characters. To support handoff * of API client memory, the chars are allocated separately from the length, * necessitating a pointer after the count, to form a separately allocated * string descriptor. String descriptors are GC'ed, while their chars are * allocated from the malloc heap. * * When a string is treated as an object (by following it with . or []), the * runtime wraps it with a JSObject whose valueOf method returns the unwrapped * string descriptor. */ #include #include "jspubtd.h" #include "jsprvtd.h" #include "jshash.h" JS_BEGIN_EXTERN_C /* * The original GC-thing "string" type, a flat character string owned by its * GC-thing descriptor. The chars member points to a vector having byte size * (length + 1) * sizeof(jschar), terminated at index length by a zero jschar. * The terminator is purely a backstop, in case the chars pointer flows out to * native code that requires \u0000 termination. * * NB: Always use the JSSTRING_LENGTH and JSSTRING_CHARS accessor macros, * unless you guard str->member uses with !JSSTRING_IS_DEPENDENT(str). */ struct JSString { size_t length; jschar *chars; }; /* * Overlay structure for a string that depends on another string's characters. * Distinguished by the JSSTRFLAG_DEPENDENT bit being set in length. The base * member may point to another dependent string if JSSTRING_CHARS has not been * called yet. The length chars in a dependent string are stored starting at * base->chars + start, and are not necessarily zero-terminated. If start is * 0, it is not stored, length is a full size_t (minus the JSSTRFLAG_* bits in * the high two positions), and the JSSTRFLAG_PREFIX flag is set. */ struct JSDependentString { size_t length; JSString *base; }; /* Definitions for flags stored in the high order bits of JSString.length. */ #define JSSTRFLAG_BITS 2 #define JSSTRFLAG_SHIFT(flg) ((size_t)(flg) << JSSTRING_LENGTH_BITS) #define JSSTRFLAG_MASK JSSTRFLAG_SHIFT(JS_BITMASK(JSSTRFLAG_BITS)) #define JSSTRFLAG_DEPENDENT JSSTRFLAG_SHIFT(1) #define JSSTRFLAG_PREFIX JSSTRFLAG_SHIFT(2) /* Universal JSString type inquiry and accessor macros. */ #define JSSTRING_BIT(n) ((size_t)1 << (n)) #define JSSTRING_BITMASK(n) (JSSTRING_BIT(n) - 1) #define JSSTRING_HAS_FLAG(str,flg) ((str)->length & (flg)) #define JSSTRING_IS_DEPENDENT(str) JSSTRING_HAS_FLAG(str, JSSTRFLAG_DEPENDENT) #define JSSTRING_IS_PREFIX(str) JSSTRING_HAS_FLAG(str, JSSTRFLAG_PREFIX) #define JSSTRING_CHARS(str) (JSSTRING_IS_DEPENDENT(str) \ ? JSSTRDEP_CHARS(str) \ : (str)->chars) #define JSSTRING_LENGTH(str) (JSSTRING_IS_DEPENDENT(str) \ ? JSSTRDEP_LENGTH(str) \ : (str)->length) #define JSSTRING_LENGTH_BITS (sizeof(size_t) * JS_BITS_PER_BYTE \ - JSSTRFLAG_BITS) #define JSSTRING_LENGTH_MASK JSSTRING_BITMASK(JSSTRING_LENGTH_BITS) /* Specific JSDependentString shift/mask accessor and mutator macros. */ #define JSSTRDEP_START_BITS (JSSTRING_LENGTH_BITS-JSSTRDEP_LENGTH_BITS) #define JSSTRDEP_START_SHIFT JSSTRDEP_LENGTH_BITS #define JSSTRDEP_START_MASK JSSTRING_BITMASK(JSSTRDEP_START_BITS) #define JSSTRDEP_LENGTH_BITS (JSSTRING_LENGTH_BITS / 2) #define JSSTRDEP_LENGTH_MASK JSSTRING_BITMASK(JSSTRDEP_LENGTH_BITS) #define JSSTRDEP(str) ((JSDependentString *)(str)) #define JSSTRDEP_START(str) (JSSTRING_IS_PREFIX(str) ? 0 \ : ((JSSTRDEP(str)->length \ >> JSSTRDEP_START_SHIFT) \ & JSSTRDEP_START_MASK)) #define JSSTRDEP_LENGTH(str) (JSSTRDEP(str)->length \ & (JSSTRING_IS_PREFIX(str) \ ? JSSTRING_LENGTH_MASK \ : JSSTRDEP_LENGTH_MASK)) #define JSSTRDEP_SET_START_AND_LENGTH(str,off,len) \ (JSSTRDEP(str)->length = JSSTRFLAG_DEPENDENT \ | ((off) << JSSTRDEP_START_SHIFT) \ | (len)) #define JSPREFIX_SET_LENGTH(str,len) \ (JSSTRDEP(str)->length = JSSTRFLAG_DEPENDENT | JSSTRFLAG_PREFIX | (len)) #define JSSTRDEP_BASE(str) (JSSTRDEP(str)->base) #define JSSTRDEP_SET_BASE(str,bstr) (JSSTRDEP(str)->base = (bstr)) #define JSPREFIX_BASE(str) JSSTRDEP_BASE(str) #define JSPREFIX_SET_BASE(str,bstr) JSSTRDEP_SET_BASE(str,bstr) #define JSSTRDEP_CHARS(str) \ (JSSTRING_IS_DEPENDENT(JSSTRDEP_BASE(str)) \ ? js_GetDependentStringChars(str) \ : JSSTRDEP_BASE(str)->chars + JSSTRDEP_START(str)) extern size_t js_MinimizeDependentStrings(JSString *str, int level, JSString **basep); extern jschar * js_GetDependentStringChars(JSString *str); extern jschar * js_GetStringChars(JSString *str); extern JSString * js_ConcatStrings(JSContext *cx, JSString *left, JSString *right); extern const jschar * js_UndependString(JSContext *cx, JSString *str); struct JSSubString { size_t length; const jschar *chars; }; extern jschar js_empty_ucstr[]; extern JSSubString js_EmptySubString; /* Unicode character attribute lookup tables. */ extern const uint8 js_X[]; extern const uint8 js_Y[]; extern const uint32 js_A[]; /* Enumerated Unicode general category types. */ typedef enum JSCharType { JSCT_UNASSIGNED = 0, JSCT_UPPERCASE_LETTER = 1, JSCT_LOWERCASE_LETTER = 2, JSCT_TITLECASE_LETTER = 3, JSCT_MODIFIER_LETTER = 4, JSCT_OTHER_LETTER = 5, JSCT_NON_SPACING_MARK = 6, JSCT_ENCLOSING_MARK = 7, JSCT_COMBINING_SPACING_MARK = 8, JSCT_DECIMAL_DIGIT_NUMBER = 9, JSCT_LETTER_NUMBER = 10, JSCT_OTHER_NUMBER = 11, JSCT_SPACE_SEPARATOR = 12, JSCT_LINE_SEPARATOR = 13, JSCT_PARAGRAPH_SEPARATOR = 14, JSCT_CONTROL = 15, JSCT_FORMAT = 16, JSCT_PRIVATE_USE = 18, JSCT_SURROGATE = 19, JSCT_DASH_PUNCTUATION = 20, JSCT_START_PUNCTUATION = 21, JSCT_END_PUNCTUATION = 22, JSCT_CONNECTOR_PUNCTUATION = 23, JSCT_OTHER_PUNCTUATION = 24, JSCT_MATH_SYMBOL = 25, JSCT_CURRENCY_SYMBOL = 26, JSCT_MODIFIER_SYMBOL = 27, JSCT_OTHER_SYMBOL = 28 } JSCharType; /* Character classifying and mapping macros, based on java.lang.Character. */ #define JS_CCODE(c) (js_A[js_Y[(js_X[(uint16)(c)>>6]<<6)|((c)&0x3F)]]) #define JS_CTYPE(c) (JS_CCODE(c) & 0x1F) #define JS_ISALPHA(c) ((((1 << JSCT_UPPERCASE_LETTER) | \ (1 << JSCT_LOWERCASE_LETTER) | \ (1 << JSCT_TITLECASE_LETTER) | \ (1 << JSCT_MODIFIER_LETTER) | \ (1 << JSCT_OTHER_LETTER)) \ >> JS_CTYPE(c)) & 1) #define JS_ISALNUM(c) ((((1 << JSCT_UPPERCASE_LETTER) | \ (1 << JSCT_LOWERCASE_LETTER) | \ (1 << JSCT_TITLECASE_LETTER) | \ (1 << JSCT_MODIFIER_LETTER) | \ (1 << JSCT_OTHER_LETTER) | \ (1 << JSCT_DECIMAL_DIGIT_NUMBER)) \ >> JS_CTYPE(c)) & 1) /* A unicode letter, suitable for use in an identifier. */ #define JS_ISLETTER(c) ((((1 << JSCT_UPPERCASE_LETTER) | \ (1 << JSCT_LOWERCASE_LETTER) | \ (1 << JSCT_TITLECASE_LETTER) | \ (1 << JSCT_MODIFIER_LETTER) | \ (1 << JSCT_OTHER_LETTER) | \ (1 << JSCT_LETTER_NUMBER)) \ >> JS_CTYPE(c)) & 1) /* * 'IdentifierPart' from ECMA grammar, is Unicode letter or combining mark or * digit or connector punctuation. */ #define JS_ISIDPART(c) ((((1 << JSCT_UPPERCASE_LETTER) | \ (1 << JSCT_LOWERCASE_LETTER) | \ (1 << JSCT_TITLECASE_LETTER) | \ (1 << JSCT_MODIFIER_LETTER) | \ (1 << JSCT_OTHER_LETTER) | \ (1 << JSCT_LETTER_NUMBER) | \ (1 << JSCT_NON_SPACING_MARK) | \ (1 << JSCT_COMBINING_SPACING_MARK) | \ (1 << JSCT_DECIMAL_DIGIT_NUMBER) | \ (1 << JSCT_CONNECTOR_PUNCTUATION)) \ >> JS_CTYPE(c)) & 1) /* Unicode control-format characters, ignored in input */ #define JS_ISFORMAT(c) (((1 << JSCT_FORMAT) >> JS_CTYPE(c)) & 1) /* * Per ECMA-262 15.10.2.6, these characters are the only ones that make up a * "word", as far as a RegExp is concerned. If we want a Unicode-friendlier * definition of "word", we should rename this macro to something regexp-y. */ #define JS_ISWORD(c) ((c) < 128 && (isalnum(c) || (c) == '_')) #define JS_ISIDSTART(c) (JS_ISLETTER(c) || (c) == '_' || (c) == '$') #define JS_ISIDENT(c) (JS_ISIDPART(c) || (c) == '_' || (c) == '$') #define JS_ISXMLSPACE(c) ((c) == ' ' || (c) == '\t' || (c) == '\r' || \ (c) == '\n') #define JS_ISXMLNSSTART(c) ((JS_CCODE(c) & 0x00000100) || (c) == '_') #define JS_ISXMLNS(c) ((JS_CCODE(c) & 0x00000080) || (c) == '.' || \ (c) == '-' || (c) == '_') #define JS_ISXMLNAMESTART(c) (JS_ISXMLNSSTART(c) || (c) == ':') #define JS_ISXMLNAME(c) (JS_ISXMLNS(c) || (c) == ':') #define JS_ISDIGIT(c) (JS_CTYPE(c) == JSCT_DECIMAL_DIGIT_NUMBER) /* XXXbe unify on A/X/Y tbls, avoid ctype.h? */ /* XXXbe fs, etc. ? */ #define JS_ISSPACE(c) ((JS_CCODE(c) & 0x00070000) == 0x00040000) #define JS_ISPRINT(c) ((c) < 128 && isprint(c)) #define JS_ISUPPER(c) (JS_CTYPE(c) == JSCT_UPPERCASE_LETTER) #define JS_ISLOWER(c) (JS_CTYPE(c) == JSCT_LOWERCASE_LETTER) #define JS_TOUPPER(c) ((jschar) ((JS_CCODE(c) & 0x00100000) \ ? (c) - ((int32)JS_CCODE(c) >> 22) \ : (c))) #define JS_TOLOWER(c) ((jschar) ((JS_CCODE(c) & 0x00200000) \ ? (c) + ((int32)JS_CCODE(c) >> 22) \ : (c))) /* * Shorthands for ASCII (7-bit) decimal and hex conversion. * Manually inline isdigit for performance; MSVC doesn't do this for us. */ #define JS7_ISDEC(c) ((((unsigned)(c)) - '0') <= 9) #define JS7_UNDEC(c) ((c) - '0') #define JS7_ISHEX(c) ((c) < 128 && isxdigit(c)) #define JS7_UNHEX(c) (uintN)(JS7_ISDEC(c) ? (c) - '0' : 10 + tolower(c) - 'a') #define JS7_ISLET(c) ((c) < 128 && isalpha(c)) /* Initialize per-runtime string state for the first context in the runtime. */ extern JSBool js_InitRuntimeStringState(JSContext *cx); extern void js_FinishRuntimeStringState(JSContext *cx); extern void js_FinishDeflatedStringCache(JSRuntime *rt); /* Initialize the String class, returning its prototype object. */ extern JSClass js_StringClass; extern JSObject * js_InitStringClass(JSContext *cx, JSObject *obj); extern const char js_escape_str[]; extern const char js_unescape_str[]; extern const char js_uneval_str[]; extern const char js_decodeURI_str[]; extern const char js_encodeURI_str[]; extern const char js_decodeURIComponent_str[]; extern const char js_encodeURIComponent_str[]; /* GC-allocate a string descriptor for the given malloc-allocated chars. */ extern JSString * js_NewString(JSContext *cx, jschar *chars, size_t length, uintN gcflag); extern JSString * js_NewDependentString(JSContext *cx, JSString *base, size_t start, size_t length, uintN gcflag); /* Copy a counted string and GC-allocate a descriptor for it. */ extern JSString * js_NewStringCopyN(JSContext *cx, const jschar *s, size_t n, uintN gcflag); /* Copy a C string and GC-allocate a descriptor for it. */ extern JSString * js_NewStringCopyZ(JSContext *cx, const jschar *s, uintN gcflag); /* Free the chars held by str when it is finalized by the GC. */ extern void js_FinalizeString(JSContext *cx, JSString *str); extern void js_FinalizeStringRT(JSRuntime *rt, JSString *str); /* Wrap a string value in a String object. */ extern JSObject * js_StringToObject(JSContext *cx, JSString *str); /* * Convert a value to a printable C string. */ typedef JSString *(*JSValueToStringFun)(JSContext *cx, jsval v); extern JS_FRIEND_API(const char *) js_ValueToPrintable(JSContext *cx, jsval v, JSValueToStringFun v2sfun); #define js_ValueToPrintableString(cx,v) \ js_ValueToPrintable(cx, v, js_ValueToString) #define js_ValueToPrintableSource(cx,v) \ js_ValueToPrintable(cx, v, js_ValueToSource) /* * Convert a value to a string, returning null after reporting an error, * otherwise returning a new string reference. */ extern JS_FRIEND_API(JSString *) js_ValueToString(JSContext *cx, jsval v); /* * Convert a value to its source expression, returning null after reporting * an error, otherwise returning a new string reference. */ extern JS_FRIEND_API(JSString *) js_ValueToSource(JSContext *cx, jsval v); #ifdef HT_ENUMERATE_NEXT /* XXX don't require jshash.h */ /* * Compute a hash function from str. */ extern JSHashNumber js_HashString(JSString *str); #endif /* * Return less than, equal to, or greater than zero depending on whether * str1 is less than, equal to, or greater than str2. */ extern intN js_CompareStrings(JSString *str1, JSString *str2); /* * Test if strings are equal. */ extern JSBool js_EqualStrings(JSString *str1, JSString *str2); /* * Boyer-Moore-Horspool superlinear search for pat:patlen in text:textlen. * The patlen argument must be positive and no greater than BMH_PATLEN_MAX. * The start argument tells where in text to begin the search. * * Return the index of pat in text, or -1 if not found. */ #define BMH_CHARSET_SIZE 256 /* ISO-Latin-1 */ #define BMH_PATLEN_MAX 255 /* skip table element is uint8 */ #define BMH_BAD_PATTERN (-2) /* return value if pat is not ISO-Latin-1 */ extern jsint js_BoyerMooreHorspool(const jschar *text, jsint textlen, const jschar *pat, jsint patlen, jsint start); extern size_t js_strlen(const jschar *s); extern jschar * js_strchr(const jschar *s, jschar c); extern jschar * js_strchr_limit(const jschar *s, jschar c, const jschar *limit); #define js_strncpy(t, s, n) memcpy((t), (s), (n) * sizeof(jschar)) /* * Return s advanced past any Unicode white space characters. */ extern const jschar * js_SkipWhiteSpace(const jschar *s); /* * Inflate bytes to JS chars and vice versa. Report out of memory via cx * and return null on error, otherwise return the jschar or byte vector that * was JS_malloc'ed. length is updated with the length of the new string in jschars. */ extern jschar * js_InflateString(JSContext *cx, const char *bytes, size_t *length); extern char * js_DeflateString(JSContext *cx, const jschar *chars, size_t length); /* * Inflate bytes to JS chars into a buffer. * 'chars' must be large enough for 'length' jschars. * The buffer is NOT null-terminated. * cx may be NULL, which means no errors are thrown. * The destination length needs to be initialized with the buffer size, takes * the number of chars moved. */ extern JSBool js_InflateStringToBuffer(JSContext* cx, const char *bytes, size_t length, jschar *chars, size_t* charsLength); /* * Deflate JS chars to bytes into a buffer. * 'bytes' must be large enough for 'length chars. * The buffer is NOT null-terminated. * cx may be NULL, which means no errors are thrown. * The destination length needs to be initialized with the buffer size, takes * the number of bytes moved. */ extern JSBool js_DeflateStringToBuffer(JSContext* cx, const jschar *chars, size_t charsLength, char *bytes, size_t* length); /* * Associate bytes with str in the deflated string cache, returning true on * successful association, false on out of memory. */ extern JSBool js_SetStringBytes(JSRuntime *rt, JSString *str, char *bytes, size_t length); /* * Find or create a deflated string cache entry for str that contains its * characters chopped from Unicode code points into bytes. */ extern char * js_GetStringBytes(JSRuntime *rt, JSString *str); /* Remove a deflated string cache entry associated with str if any. */ extern void js_PurgeDeflatedStringCache(JSRuntime *rt, JSString *str); JSBool js_str_escape(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval); /* * Convert one UCS-4 char and write it into a UTF-8 buffer, which must be at * least 6 bytes long. Return the number of UTF-8 bytes of data written. */ extern int js_OneUcs4ToUtf8Char(uint8 *utf8Buffer, uint32 ucs4Char); JS_END_EXTERN_C #endif /* jsstr_h___ */ pacparser-1.4.5/src/spidermonkey/js/src/jstypes.h000066400000000000000000000404251464010763600220720ustar00rootroot00000000000000/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* ***** BEGIN LICENSE BLOCK ***** * Version: MPL 1.1/GPL 2.0/LGPL 2.1 * * The contents of this file are subject to the Mozilla Public License Version * 1.1 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * http://www.mozilla.org/MPL/ * * Software distributed under the License is distributed on an "AS IS" basis, * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License * for the specific language governing rights and limitations under the * License. * * The Original Code is Mozilla Communicator client code, released * March 31, 1998. * * The Initial Developer of the Original Code is * Netscape Communications Corporation. * Portions created by the Initial Developer are Copyright (C) 1998 * the Initial Developer. All Rights Reserved. * * Contributor(s): * IBM Corp. * * Alternatively, the contents of this file may be used under the terms of * either of the GNU General Public License Version 2 or later (the "GPL"), * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), * in which case the provisions of the GPL or the LGPL are applicable instead * of those above. If you wish to allow use of your version of this file only * under the terms of either the GPL or the LGPL, and not to allow others to * use your version of this file under the terms of the MPL, indicate your * decision by deleting the provisions above and replace them with the notice * and other provisions required by the GPL or the LGPL. If you do not delete * the provisions above, a recipient may use your version of this file under * the terms of any one of the MPL, the GPL or the LGPL. * * ***** END LICENSE BLOCK ***** */ /* ** File: jstypes.h ** Description: Definitions of NSPR's basic types ** ** Prototypes and macros used to make up for deficiencies in ANSI environments ** that we have found. ** ** Since we do not wrap and all the other standard headers, authors ** of portable code will not know in general that they need these definitions. ** Instead of requiring these authors to find the dependent uses in their code ** and take the following steps only in those C files, we take steps once here ** for all C files. **/ #ifndef jstypes_h___ #define jstypes_h___ #include /*********************************************************************** ** MACROS: JS_EXTERN_API ** JS_EXPORT_API ** DESCRIPTION: ** These are only for externally visible routines and globals. For ** internal routines, just use "extern" for type checking and that ** will not export internal cross-file or forward-declared symbols. ** Define a macro for declaring procedures return types. We use this to ** deal with windoze specific type hackery for DLL definitions. Use ** JS_EXTERN_API when the prototype for the method is declared. Use ** JS_EXPORT_API for the implementation of the method. ** ** Example: ** in dowhim.h ** JS_EXTERN_API( void ) DoWhatIMean( void ); ** in dowhim.c ** JS_EXPORT_API( void ) DoWhatIMean( void ) { return; } ** ** ***********************************************************************/ #ifdef WIN32 /* These also work for __MWERKS__ */ #define JS_EXTERN_API(__type) extern __declspec(dllexport) __type #define JS_EXPORT_API(__type) __declspec(dllexport) __type #define JS_EXTERN_DATA(__type) extern __declspec(dllexport) __type #define JS_EXPORT_DATA(__type) __declspec(dllexport) __type #define JS_DLL_CALLBACK #define JS_STATIC_DLL_CALLBACK(__x) static __x #elif defined(XP_OS2) && defined(__declspec) #define JS_EXTERN_API(__type) extern __declspec(dllexport) __type #define JS_EXPORT_API(__type) __declspec(dllexport) __type #define JS_EXTERN_DATA(__type) extern __declspec(dllexport) __type #define JS_EXPORT_DATA(__type) __declspec(dllexport) __type #define JS_DLL_CALLBACK #define JS_STATIC_DLL_CALLBACK(__x) static __x #elif defined(WIN16) #ifdef _WINDLL #define JS_EXTERN_API(__type) extern __type _cdecl _export _loadds #define JS_EXPORT_API(__type) __type _cdecl _export _loadds #define JS_EXTERN_DATA(__type) extern __type _export #define JS_EXPORT_DATA(__type) __type _export #define JS_DLL_CALLBACK __cdecl __loadds #define JS_STATIC_DLL_CALLBACK(__x) static __x CALLBACK #else /* this must be .EXE */ #define JS_EXTERN_API(__type) extern __type _cdecl _export #define JS_EXPORT_API(__type) __type _cdecl _export #define JS_EXTERN_DATA(__type) extern __type _export #define JS_EXPORT_DATA(__type) __type _export #define JS_DLL_CALLBACK __cdecl __loadds #define JS_STATIC_DLL_CALLBACK(__x) __x JS_DLL_CALLBACK #endif /* _WINDLL */ #else /* Unix */ #ifdef HAVE_VISIBILITY_ATTRIBUTE #define JS_EXTERNAL_VIS __attribute__((visibility ("default"))) #else #define JS_EXTERNAL_VIS #endif #define JS_EXTERN_API(__type) extern JS_EXTERNAL_VIS __type #define JS_EXPORT_API(__type) JS_EXTERNAL_VIS __type #define JS_EXTERN_DATA(__type) extern JS_EXTERNAL_VIS __type #define JS_EXPORT_DATA(__type) JS_EXTERNAL_VIS __type #define JS_DLL_CALLBACK #define JS_STATIC_DLL_CALLBACK(__x) static __x #endif #ifdef _WIN32 # if defined(__MWERKS__) || defined(__GNUC__) # define JS_IMPORT_API(__x) __x # else # define JS_IMPORT_API(__x) __declspec(dllimport) __x # endif #elif defined(XP_OS2) && defined(__declspec) # define JS_IMPORT_API(__x) __declspec(dllimport) __x #else # define JS_IMPORT_API(__x) JS_EXPORT_API (__x) #endif #if defined(_WIN32) && !defined(__MWERKS__) # define JS_IMPORT_DATA(__x) __declspec(dllimport) __x #elif defined(XP_OS2) && defined(__declspec) # define JS_IMPORT_DATA(__x) __declspec(dllimport) __x #else # define JS_IMPORT_DATA(__x) JS_EXPORT_DATA (__x) #endif /* * The linkage of JS API functions differs depending on whether the file is * used within the JS library or not. Any source file within the JS * interpreter should define EXPORT_JS_API whereas any client of the library * should not. */ #ifdef EXPORT_JS_API #define JS_PUBLIC_API(t) JS_EXPORT_API(t) #define JS_PUBLIC_DATA(t) JS_EXPORT_DATA(t) #else #define JS_PUBLIC_API(t) JS_IMPORT_API(t) #define JS_PUBLIC_DATA(t) JS_IMPORT_DATA(t) #endif #define JS_FRIEND_API(t) JS_PUBLIC_API(t) #define JS_FRIEND_DATA(t) JS_PUBLIC_DATA(t) #ifdef _WIN32 # define JS_INLINE __inline #elif defined(__GNUC__) # define JS_INLINE #else # define JS_INLINE #endif /*********************************************************************** ** MACROS: JS_BEGIN_MACRO ** JS_END_MACRO ** DESCRIPTION: ** Macro body brackets so that macros with compound statement definitions ** behave syntactically more like functions when called. ***********************************************************************/ #define JS_BEGIN_MACRO do { #define JS_END_MACRO } while (0) /*********************************************************************** ** MACROS: JS_BEGIN_EXTERN_C ** JS_END_EXTERN_C ** DESCRIPTION: ** Macro shorthands for conditional C++ extern block delimiters. ***********************************************************************/ #ifdef __cplusplus #define JS_BEGIN_EXTERN_C extern "C" { #define JS_END_EXTERN_C } #else #define JS_BEGIN_EXTERN_C #define JS_END_EXTERN_C #endif /*********************************************************************** ** MACROS: JS_BIT ** JS_BITMASK ** DESCRIPTION: ** Bit masking macros. XXX n must be <= 31 to be portable ***********************************************************************/ #define JS_BIT(n) ((JSUint32)1 << (n)) #define JS_BITMASK(n) (JS_BIT(n) - 1) /*********************************************************************** ** MACROS: JS_PTR_TO_INT32 ** JS_PTR_TO_UINT32 ** JS_INT32_TO_PTR ** JS_UINT32_TO_PTR ** DESCRIPTION: ** Integer to pointer and pointer to integer conversion macros. ***********************************************************************/ #define JS_PTR_TO_INT32(x) ((jsint)((char *)(x) - (char *)0)) #define JS_PTR_TO_UINT32(x) ((jsuint)((char *)(x) - (char *)0)) #define JS_INT32_TO_PTR(x) ((void *)((char *)0 + (jsint)(x))) #define JS_UINT32_TO_PTR(x) ((void *)((char *)0 + (jsuint)(x))) /*********************************************************************** ** MACROS: JS_HOWMANY ** JS_ROUNDUP ** JS_MIN ** JS_MAX ** DESCRIPTION: ** Commonly used macros for operations on compatible types. ***********************************************************************/ #define JS_HOWMANY(x,y) (((x)+(y)-1)/(y)) #define JS_ROUNDUP(x,y) (JS_HOWMANY(x,y)*(y)) #define JS_MIN(x,y) ((x)<(y)?(x):(y)) #define JS_MAX(x,y) ((x)>(y)?(x):(y)) #if (defined(XP_WIN) && !defined(CROSS_COMPILE)) || defined (WINCE) # include "jscpucfg.h" /* Use standard Mac or Windows configuration */ #elif defined(XP_UNIX) || defined(XP_BEOS) || defined(XP_OS2) || defined(CROSS_COMPILE) # include "jsautocfg.h" /* Use auto-detected configuration */ # include "jsosdep.h" /* ...and platform-specific flags */ #else # error "Must define one of XP_BEOS, XP_OS2, XP_WIN or XP_UNIX" #endif JS_BEGIN_EXTERN_C /************************************************************************ ** TYPES: JSUint8 ** JSInt8 ** DESCRIPTION: ** The int8 types are known to be 8 bits each. There is no type that ** is equivalent to a plain "char". ************************************************************************/ #if JS_BYTES_PER_BYTE == 1 typedef unsigned char JSUint8; typedef signed char JSInt8; #else #error No suitable type for JSInt8/JSUint8 #endif /************************************************************************ ** TYPES: JSUint16 ** JSInt16 ** DESCRIPTION: ** The int16 types are known to be 16 bits each. ************************************************************************/ #if JS_BYTES_PER_SHORT == 2 typedef unsigned short JSUint16; typedef short JSInt16; #else #error No suitable type for JSInt16/JSUint16 #endif /************************************************************************ ** TYPES: JSUint32 ** JSInt32 ** DESCRIPTION: ** The int32 types are known to be 32 bits each. ************************************************************************/ #if JS_BYTES_PER_INT == 4 typedef unsigned int JSUint32; typedef int JSInt32; #define JS_INT32(x) x #define JS_UINT32(x) x ## U #elif JS_BYTES_PER_LONG == 4 typedef unsigned long JSUint32; typedef long JSInt32; #define JS_INT32(x) x ## L #define JS_UINT32(x) x ## UL #else #error No suitable type for JSInt32/JSUint32 #endif /************************************************************************ ** TYPES: JSUint64 ** JSInt64 ** DESCRIPTION: ** The int64 types are known to be 64 bits each. Care must be used when ** declaring variables of type JSUint64 or JSInt64. Different hardware ** architectures and even different compilers have varying support for ** 64 bit values. The only guaranteed portability requires the use of ** the JSLL_ macros (see jslong.h). ************************************************************************/ #ifdef JS_HAVE_LONG_LONG #if JS_BYTES_PER_LONG == 8 typedef long JSInt64; typedef unsigned long JSUint64; #elif defined(WIN16) typedef __int64 JSInt64; typedef unsigned __int64 JSUint64; #elif defined(WIN32) && !defined(__GNUC__) typedef __int64 JSInt64; typedef unsigned __int64 JSUint64; #else typedef long long JSInt64; typedef unsigned long long JSUint64; #endif /* JS_BYTES_PER_LONG == 8 */ #else /* !JS_HAVE_LONG_LONG */ typedef struct { #ifdef IS_LITTLE_ENDIAN JSUint32 lo, hi; #else JSUint32 hi, lo; #endif } JSInt64; typedef JSInt64 JSUint64; #endif /* !JS_HAVE_LONG_LONG */ /************************************************************************ ** TYPES: JSUintn ** JSIntn ** DESCRIPTION: ** The JSIntn types are most appropriate for automatic variables. They are ** guaranteed to be at least 16 bits, though various architectures may ** define them to be wider (e.g., 32 or even 64 bits). These types are ** never valid for fields of a structure. ************************************************************************/ #if JS_BYTES_PER_INT >= 2 typedef int JSIntn; typedef unsigned int JSUintn; #else #error 'sizeof(int)' not sufficient for platform use #endif /************************************************************************ ** TYPES: JSFloat64 ** DESCRIPTION: ** NSPR's floating point type is always 64 bits. ************************************************************************/ typedef double JSFloat64; /************************************************************************ ** TYPES: JSSize ** DESCRIPTION: ** A type for representing the size of objects. ************************************************************************/ typedef size_t JSSize; /************************************************************************ ** TYPES: JSPtrDiff ** DESCRIPTION: ** A type for pointer difference. Variables of this type are suitable ** for storing a pointer or pointer sutraction. ************************************************************************/ typedef ptrdiff_t JSPtrdiff; /************************************************************************ ** TYPES: JSUptrdiff ** DESCRIPTION: ** A type for pointer difference. Variables of this type are suitable ** for storing a pointer or pointer sutraction. ************************************************************************/ #if JS_BYTES_PER_WORD == 8 && JS_BYTES_PER_LONG != 8 typedef JSUint64 JSUptrdiff; #else typedef unsigned long JSUptrdiff; #endif /************************************************************************ ** TYPES: JSBool ** DESCRIPTION: ** Use JSBool for variables and parameter types. Use JS_FALSE and JS_TRUE ** for clarity of target type in assignments and actual arguments. Use ** 'if (bool)', 'while (!bool)', '(bool) ? x : y' etc., to test booleans ** just as you would C int-valued conditions. ************************************************************************/ typedef JSIntn JSBool; #define JS_TRUE (JSIntn)1 #define JS_FALSE (JSIntn)0 /************************************************************************ ** TYPES: JSPackedBool ** DESCRIPTION: ** Use JSPackedBool within structs where bitfields are not desireable ** but minimum and consistent overhead matters. ************************************************************************/ typedef JSUint8 JSPackedBool; /* ** A JSWord is an integer that is the same size as a void* */ #if JS_BYTES_PER_WORD == 8 && JS_BYTES_PER_LONG != 8 typedef JSInt64 JSWord; typedef JSUint64 JSUword; #else typedef long JSWord; typedef unsigned long JSUword; #endif #include "jsotypes.h" /*********************************************************************** ** MACROS: JS_LIKELY ** JS_UNLIKELY ** DESCRIPTION: ** These macros allow you to give a hint to the compiler about branch ** probability so that it can better optimize. Use them like this: ** ** if (JS_LIKELY(v == 1)) { ** ... expected code path ... ** } ** ** if (JS_UNLIKELY(v == 0)) { ** ... non-expected code path ... ** } ** ***********************************************************************/ #if defined(__GNUC__) && (__GNUC__ > 2) #define JS_LIKELY(x) (__builtin_expect((x), 1)) #define JS_UNLIKELY(x) (__builtin_expect((x), 0)) #else #define JS_LIKELY(x) (x) #define JS_UNLIKELY(x) (x) #endif /*********************************************************************** ** MACROS: JS_ARRAY_LENGTH ** JS_ARRAY_END ** DESCRIPTION: ** Macros to get the number of elements and the pointer to one past the ** last element of a C array. Use them like this: ** ** jschar buf[10], *s; ** JSString *str; ** ... ** for (s = buf; s != JS_ARRAY_END(buf); ++s) *s = ...; ** ... ** str = JS_NewStringCopyN(cx, buf, JS_ARRAY_LENGTH(buf)); ** ... ** ***********************************************************************/ #define JS_ARRAY_LENGTH(array) (sizeof (array) / sizeof (array)[0]) #define JS_ARRAY_END(array) ((array) + JS_ARRAY_LENGTH(array)) JS_END_EXTERN_C #endif /* jstypes_h___ */ pacparser-1.4.5/src/spidermonkey/js/src/jsutil.c000066400000000000000000000134231464010763600216740ustar00rootroot00000000000000/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- * * ***** BEGIN LICENSE BLOCK ***** * Version: MPL 1.1/GPL 2.0/LGPL 2.1 * * The contents of this file are subject to the Mozilla Public License Version * 1.1 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * http://www.mozilla.org/MPL/ * * Software distributed under the License is distributed on an "AS IS" basis, * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License * for the specific language governing rights and limitations under the * License. * * The Original Code is Mozilla Communicator client code, released * March 31, 1998. * * The Initial Developer of the Original Code is * Netscape Communications Corporation. * Portions created by the Initial Developer are Copyright (C) 1998 * the Initial Developer. All Rights Reserved. * * Contributor(s): * IBM Corp. * * Alternatively, the contents of this file may be used under the terms of * either of the GNU General Public License Version 2 or later (the "GPL"), * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), * in which case the provisions of the GPL or the LGPL are applicable instead * of those above. If you wish to allow use of your version of this file only * under the terms of either the GPL or the LGPL, and not to allow others to * use your version of this file under the terms of the MPL, indicate your * decision by deleting the provisions above and replace them with the notice * and other provisions required by the GPL or the LGPL. If you do not delete * the provisions above, a recipient may use your version of this file under * the terms of any one of the MPL, the GPL or the LGPL. * * ***** END LICENSE BLOCK ***** */ /* * PR assertion checker. */ #include "jsstddef.h" #include #include #include "jstypes.h" #include "jsutil.h" #ifdef WIN32 # include #endif JS_PUBLIC_API(void) JS_Assert(const char *s, const char *file, JSIntn ln) { fprintf(stderr, "Assertion failure: %s, at %s:%d\n", s, file, ln); #if defined(WIN32) DebugBreak(); exit(3); #elif defined(XP_OS2) || (defined(__GNUC__) && defined(__i386)) asm("int $3"); #endif abort(); } #if defined DEBUG_notme && defined XP_UNIX #define __USE_GNU 1 #include #include #include "jshash.h" #include "jsprf.h" JSCallsite js_calltree_root = {0, NULL, NULL, 0, NULL, NULL, NULL, NULL}; static JSCallsite * CallTree(void **bp) { void **bpup, **bpdown, *pc; JSCallsite *parent, *site, **csp; Dl_info info; int ok, offset; const char *symbol; char *method; /* Reverse the stack frame list to avoid recursion. */ bpup = NULL; for (;;) { bpdown = (void**) bp[0]; bp[0] = (void*) bpup; if ((void**) bpdown[0] < bpdown) break; bpup = bp; bp = bpdown; } /* Reverse the stack again, finding and building a path in the tree. */ parent = &js_calltree_root; do { bpup = (void**) bp[0]; bp[0] = (void*) bpdown; pc = bp[1]; csp = &parent->kids; while ((site = *csp) != NULL) { if (site->pc == pc) { /* Put the most recently used site at the front of siblings. */ *csp = site->siblings; site->siblings = parent->kids; parent->kids = site; /* Site already built -- go up the stack. */ goto upward; } csp = &site->siblings; } /* Check for recursion: see if pc is on our ancestor line. */ for (site = parent; site; site = site->parent) { if (site->pc == pc) goto upward; } /* * Not in tree at all: let's find our symbolic callsite info. * XXX static syms are masked by nearest lower global */ info.dli_fname = info.dli_sname = NULL; ok = dladdr(pc, &info); if (ok < 0) { fprintf(stderr, "dladdr failed!\n"); return NULL; } /* XXXbe sub 0x08040000? or something, see dbaron bug with tenthumbs comment */ symbol = info.dli_sname; offset = (char*)pc - (char*)info.dli_fbase; method = symbol ? strdup(symbol) : JS_smprintf("%s+%X", info.dli_fname ? info.dli_fname : "main", offset); if (!method) return NULL; /* Create a new callsite record. */ site = (JSCallsite *) malloc(sizeof(JSCallsite)); if (!site) return NULL; /* Insert the new site into the tree. */ site->pc = pc; site->name = method; site->library = info.dli_fname; site->offset = offset; site->parent = parent; site->siblings = parent->kids; parent->kids = site; site->kids = NULL; upward: parent = site; bpdown = bp; bp = bpup; } while (bp); return site; } JSCallsite * JS_Backtrace(int skip) { void **bp, **bpdown; /* Stack walking code adapted from Kipp's "leaky". */ #if defined(__i386) __asm__( "movl %%ebp, %0" : "=g"(bp)); #elif defined(__x86_64__) __asm__( "movq %%rbp, %0" : "=g"(bp)); #else /* * It would be nice if this worked uniformly, but at least on i386 and * x86_64, it stopped working with gcc 4.1, because it points to the * end of the saved registers instead of the start. */ bp = (void**) __builtin_frame_address(0); #endif while (--skip >= 0) { bpdown = (void**) *bp++; if (bpdown < bp) break; bp = bpdown; } return CallTree(bp); } #endif /* DEBUG_notme && XP_UNIX */ pacparser-1.4.5/src/spidermonkey/js/src/jsutil.h000066400000000000000000000066101464010763600217010ustar00rootroot00000000000000/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- * * ***** BEGIN LICENSE BLOCK ***** * Version: MPL 1.1/GPL 2.0/LGPL 2.1 * * The contents of this file are subject to the Mozilla Public License Version * 1.1 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * http://www.mozilla.org/MPL/ * * Software distributed under the License is distributed on an "AS IS" basis, * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License * for the specific language governing rights and limitations under the * License. * * The Original Code is Mozilla Communicator client code, released * March 31, 1998. * * The Initial Developer of the Original Code is * Netscape Communications Corporation. * Portions created by the Initial Developer are Copyright (C) 1998 * the Initial Developer. All Rights Reserved. * * Contributor(s): * * Alternatively, the contents of this file may be used under the terms of * either of the GNU General Public License Version 2 or later (the "GPL"), * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), * in which case the provisions of the GPL or the LGPL are applicable instead * of those above. If you wish to allow use of your version of this file only * under the terms of either the GPL or the LGPL, and not to allow others to * use your version of this file under the terms of the MPL, indicate your * decision by deleting the provisions above and replace them with the notice * and other provisions required by the GPL or the LGPL. If you do not delete * the provisions above, a recipient may use your version of this file under * the terms of any one of the MPL, the GPL or the LGPL. * * ***** END LICENSE BLOCK ***** */ /* * PR assertion checker. */ #ifndef jsutil_h___ #define jsutil_h___ JS_BEGIN_EXTERN_C #ifdef DEBUG extern JS_PUBLIC_API(void) JS_Assert(const char *s, const char *file, JSIntn ln); #define JS_ASSERT(_expr) \ ((_expr)?((void)0):JS_Assert(# _expr,__FILE__,__LINE__)) #define JS_NOT_REACHED(_reasonStr) \ JS_Assert(_reasonStr,__FILE__,__LINE__) #else #define JS_ASSERT(expr) ((void) 0) #define JS_NOT_REACHED(reasonStr) #endif /* defined(DEBUG) */ /* * Compile-time assert. "condition" must be a constant expression. * The macro should be used only once per source line in places where * a "typedef" declaration is allowed. */ #define JS_STATIC_ASSERT(condition) \ JS_STATIC_ASSERT_IMPL(condition, __LINE__) #define JS_STATIC_ASSERT_IMPL(condition, line) \ JS_STATIC_ASSERT_IMPL2(condition, line) #define JS_STATIC_ASSERT_IMPL2(condition, line) \ typedef int js_static_assert_line_##line[(condition) ? 1 : -1] /* ** Abort the process in a non-graceful manner. This will cause a core file, ** call to the debugger or other moral equivalent as well as causing the ** entire process to stop. */ extern JS_PUBLIC_API(void) JS_Abort(void); #ifdef XP_UNIX typedef struct JSCallsite JSCallsite; struct JSCallsite { uint32 pc; char *name; const char *library; int offset; JSCallsite *parent; JSCallsite *siblings; JSCallsite *kids; void *handy; }; extern JSCallsite *JS_Backtrace(int skip); #endif JS_END_EXTERN_C #endif /* jsutil_h___ */ pacparser-1.4.5/src/spidermonkey/js/src/jsxdrapi.c000066400000000000000000000555071464010763600222170ustar00rootroot00000000000000/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- * * ***** BEGIN LICENSE BLOCK ***** * Version: MPL 1.1/GPL 2.0/LGPL 2.1 * * The contents of this file are subject to the Mozilla Public License Version * 1.1 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * http://www.mozilla.org/MPL/ * * Software distributed under the License is distributed on an "AS IS" basis, * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License * for the specific language governing rights and limitations under the * License. * * The Original Code is Mozilla Communicator client code, released * March 31, 1998. * * The Initial Developer of the Original Code is * Netscape Communications Corporation. * Portions created by the Initial Developer are Copyright (C) 1998 * the Initial Developer. All Rights Reserved. * * Contributor(s): * * Alternatively, the contents of this file may be used under the terms of * either of the GNU General Public License Version 2 or later (the "GPL"), * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), * in which case the provisions of the GPL or the LGPL are applicable instead * of those above. If you wish to allow use of your version of this file only * under the terms of either the GPL or the LGPL, and not to allow others to * use your version of this file under the terms of the MPL, indicate your * decision by deleting the provisions above and replace them with the notice * and other provisions required by the GPL or the LGPL. If you do not delete * the provisions above, a recipient may use your version of this file under * the terms of any one of the MPL, the GPL or the LGPL. * * ***** END LICENSE BLOCK ***** */ #include "jsstddef.h" #include "jsconfig.h" #if JS_HAS_XDR #include #include "jstypes.h" #include "jsutil.h" /* Added by JSIFY */ #include "jsdhash.h" #include "jsprf.h" #include "jsapi.h" #include "jscntxt.h" #include "jsnum.h" #include "jsobj.h" /* js_XDRObject */ #include "jsscript.h" /* js_XDRScript */ #include "jsstr.h" #include "jsxdrapi.h" #ifdef DEBUG #define DBG(x) x #else #define DBG(x) ((void)0) #endif typedef struct JSXDRMemState { JSXDRState state; char *base; uint32 count; uint32 limit; } JSXDRMemState; #define MEM_BLOCK 8192 #define MEM_PRIV(xdr) ((JSXDRMemState *)(xdr)) #define MEM_BASE(xdr) (MEM_PRIV(xdr)->base) #define MEM_COUNT(xdr) (MEM_PRIV(xdr)->count) #define MEM_LIMIT(xdr) (MEM_PRIV(xdr)->limit) #define MEM_LEFT(xdr, bytes) \ JS_BEGIN_MACRO \ if ((xdr)->mode == JSXDR_DECODE && \ MEM_COUNT(xdr) + bytes > MEM_LIMIT(xdr)) { \ JS_ReportErrorNumber((xdr)->cx, js_GetErrorMessage, NULL, \ JSMSG_END_OF_DATA); \ return 0; \ } \ JS_END_MACRO #define MEM_NEED(xdr, bytes) \ JS_BEGIN_MACRO \ if ((xdr)->mode == JSXDR_ENCODE) { \ if (MEM_LIMIT(xdr) && \ MEM_COUNT(xdr) + bytes > MEM_LIMIT(xdr)) { \ uint32 limit_ = JS_ROUNDUP(MEM_COUNT(xdr) + bytes, MEM_BLOCK);\ void *data_ = JS_realloc((xdr)->cx, MEM_BASE(xdr), limit_); \ if (!data_) \ return 0; \ MEM_BASE(xdr) = data_; \ MEM_LIMIT(xdr) = limit_; \ } \ } else { \ MEM_LEFT(xdr, bytes); \ } \ JS_END_MACRO #define MEM_DATA(xdr) ((void *)(MEM_BASE(xdr) + MEM_COUNT(xdr))) #define MEM_INCR(xdr,bytes) (MEM_COUNT(xdr) += (bytes)) static JSBool mem_get32(JSXDRState *xdr, uint32 *lp) { MEM_LEFT(xdr, 4); *lp = *(uint32 *)MEM_DATA(xdr); MEM_INCR(xdr, 4); return JS_TRUE; } static JSBool mem_set32(JSXDRState *xdr, uint32 *lp) { MEM_NEED(xdr, 4); *(uint32 *)MEM_DATA(xdr) = *lp; MEM_INCR(xdr, 4); return JS_TRUE; } static JSBool mem_getbytes(JSXDRState *xdr, char *bytes, uint32 len) { MEM_LEFT(xdr, len); memcpy(bytes, MEM_DATA(xdr), len); MEM_INCR(xdr, len); return JS_TRUE; } static JSBool mem_setbytes(JSXDRState *xdr, char *bytes, uint32 len) { MEM_NEED(xdr, len); memcpy(MEM_DATA(xdr), bytes, len); MEM_INCR(xdr, len); return JS_TRUE; } static void * mem_raw(JSXDRState *xdr, uint32 len) { void *data; if (xdr->mode == JSXDR_ENCODE) { MEM_NEED(xdr, len); } else if (xdr->mode == JSXDR_DECODE) { MEM_LEFT(xdr, len); } data = MEM_DATA(xdr); MEM_INCR(xdr, len); return data; } static JSBool mem_seek(JSXDRState *xdr, int32 offset, JSXDRWhence whence) { switch (whence) { case JSXDR_SEEK_CUR: if ((int32)MEM_COUNT(xdr) + offset < 0) { JS_ReportErrorNumber(xdr->cx, js_GetErrorMessage, NULL, JSMSG_SEEK_BEYOND_START); return JS_FALSE; } if (offset > 0) MEM_NEED(xdr, offset); MEM_COUNT(xdr) += offset; return JS_TRUE; case JSXDR_SEEK_SET: if (offset < 0) { JS_ReportErrorNumber(xdr->cx, js_GetErrorMessage, NULL, JSMSG_SEEK_BEYOND_START); return JS_FALSE; } if (xdr->mode == JSXDR_ENCODE) { if ((uint32)offset > MEM_COUNT(xdr)) MEM_NEED(xdr, offset - MEM_COUNT(xdr)); MEM_COUNT(xdr) = offset; } else { if ((uint32)offset > MEM_LIMIT(xdr)) { JS_ReportErrorNumber(xdr->cx, js_GetErrorMessage, NULL, JSMSG_SEEK_BEYOND_END); return JS_FALSE; } MEM_COUNT(xdr) = offset; } return JS_TRUE; case JSXDR_SEEK_END: if (offset >= 0 || xdr->mode == JSXDR_ENCODE || (int32)MEM_LIMIT(xdr) + offset < 0) { JS_ReportErrorNumber(xdr->cx, js_GetErrorMessage, NULL, JSMSG_END_SEEK); return JS_FALSE; } MEM_COUNT(xdr) = MEM_LIMIT(xdr) + offset; return JS_TRUE; default: { char numBuf[12]; JS_snprintf(numBuf, sizeof numBuf, "%d", whence); JS_ReportErrorNumber(xdr->cx, js_GetErrorMessage, NULL, JSMSG_WHITHER_WHENCE, numBuf); return JS_FALSE; } } } static uint32 mem_tell(JSXDRState *xdr) { return MEM_COUNT(xdr); } static void mem_finalize(JSXDRState *xdr) { JS_free(xdr->cx, MEM_BASE(xdr)); } static JSXDROps xdrmem_ops = { mem_get32, mem_set32, mem_getbytes, mem_setbytes, mem_raw, mem_seek, mem_tell, mem_finalize }; JS_PUBLIC_API(void) JS_XDRInitBase(JSXDRState *xdr, JSXDRMode mode, JSContext *cx) { xdr->mode = mode; xdr->cx = cx; xdr->registry = NULL; xdr->numclasses = xdr->maxclasses = 0; xdr->reghash = NULL; xdr->userdata = NULL; xdr->script = NULL; } JS_PUBLIC_API(JSXDRState *) JS_XDRNewMem(JSContext *cx, JSXDRMode mode) { JSXDRState *xdr = (JSXDRState *) JS_malloc(cx, sizeof(JSXDRMemState)); if (!xdr) return NULL; JS_XDRInitBase(xdr, mode, cx); if (mode == JSXDR_ENCODE) { if (!(MEM_BASE(xdr) = JS_malloc(cx, MEM_BLOCK))) { JS_free(cx, xdr); return NULL; } } else { /* XXXbe ok, so better not deref MEM_BASE(xdr) if not ENCODE */ MEM_BASE(xdr) = NULL; } xdr->ops = &xdrmem_ops; MEM_COUNT(xdr) = 0; MEM_LIMIT(xdr) = MEM_BLOCK; return xdr; } JS_PUBLIC_API(void *) JS_XDRMemGetData(JSXDRState *xdr, uint32 *lp) { if (xdr->ops != &xdrmem_ops) return NULL; *lp = MEM_COUNT(xdr); return MEM_BASE(xdr); } JS_PUBLIC_API(void) JS_XDRMemSetData(JSXDRState *xdr, void *data, uint32 len) { if (xdr->ops != &xdrmem_ops) return; MEM_LIMIT(xdr) = len; MEM_BASE(xdr) = data; MEM_COUNT(xdr) = 0; } JS_PUBLIC_API(uint32) JS_XDRMemDataLeft(JSXDRState *xdr) { if (xdr->ops != &xdrmem_ops) return 0; return MEM_LIMIT(xdr) - MEM_COUNT(xdr); } JS_PUBLIC_API(void) JS_XDRMemResetData(JSXDRState *xdr) { if (xdr->ops != &xdrmem_ops) return; MEM_COUNT(xdr) = 0; } JS_PUBLIC_API(void) JS_XDRDestroy(JSXDRState *xdr) { JSContext *cx = xdr->cx; xdr->ops->finalize(xdr); if (xdr->registry) { JS_free(cx, xdr->registry); if (xdr->reghash) JS_DHashTableDestroy(xdr->reghash); } JS_free(cx, xdr); } JS_PUBLIC_API(JSBool) JS_XDRUint8(JSXDRState *xdr, uint8 *b) { uint32 l = *b; if (!JS_XDRUint32(xdr, &l)) return JS_FALSE; *b = (uint8) l; return JS_TRUE; } JS_PUBLIC_API(JSBool) JS_XDRUint16(JSXDRState *xdr, uint16 *s) { uint32 l = *s; if (!JS_XDRUint32(xdr, &l)) return JS_FALSE; *s = (uint16) l; return JS_TRUE; } JS_PUBLIC_API(JSBool) JS_XDRUint32(JSXDRState *xdr, uint32 *lp) { JSBool ok = JS_TRUE; if (xdr->mode == JSXDR_ENCODE) { uint32 xl = JSXDR_SWAB32(*lp); ok = xdr->ops->set32(xdr, &xl); } else if (xdr->mode == JSXDR_DECODE) { ok = xdr->ops->get32(xdr, lp); *lp = JSXDR_SWAB32(*lp); } return ok; } JS_PUBLIC_API(JSBool) JS_XDRBytes(JSXDRState *xdr, char *bytes, uint32 len) { uint32 padlen; static char padbuf[JSXDR_ALIGN-1]; if (xdr->mode == JSXDR_ENCODE) { if (!xdr->ops->setbytes(xdr, bytes, len)) return JS_FALSE; } else { if (!xdr->ops->getbytes(xdr, bytes, len)) return JS_FALSE; } len = xdr->ops->tell(xdr); if (len % JSXDR_ALIGN) { padlen = JSXDR_ALIGN - (len % JSXDR_ALIGN); if (xdr->mode == JSXDR_ENCODE) { if (!xdr->ops->setbytes(xdr, padbuf, padlen)) return JS_FALSE; } else { if (!xdr->ops->seek(xdr, padlen, JSXDR_SEEK_CUR)) return JS_FALSE; } } return JS_TRUE; } /** * Convert between a C string and the XDR representation: * leading 32-bit count, then counted vector of chars, * then possibly \0 padding to multiple of 4. */ JS_PUBLIC_API(JSBool) JS_XDRCString(JSXDRState *xdr, char **sp) { uint32 len; if (xdr->mode == JSXDR_ENCODE) len = strlen(*sp); JS_XDRUint32(xdr, &len); if (xdr->mode == JSXDR_DECODE) { if (!(*sp = (char *) JS_malloc(xdr->cx, len + 1))) return JS_FALSE; } if (!JS_XDRBytes(xdr, *sp, len)) { if (xdr->mode == JSXDR_DECODE) JS_free(xdr->cx, *sp); return JS_FALSE; } if (xdr->mode == JSXDR_DECODE) { (*sp)[len] = '\0'; } else if (xdr->mode == JSXDR_FREE) { JS_free(xdr->cx, *sp); *sp = NULL; } return JS_TRUE; } JS_PUBLIC_API(JSBool) JS_XDRCStringOrNull(JSXDRState *xdr, char **sp) { uint32 null = (*sp == NULL); if (!JS_XDRUint32(xdr, &null)) return JS_FALSE; if (null) { *sp = NULL; return JS_TRUE; } return JS_XDRCString(xdr, sp); } static JSBool XDRChars(JSXDRState *xdr, jschar *chars, uint32 nchars) { uint32 i, padlen, nbytes; jschar *raw; nbytes = nchars * sizeof(jschar); padlen = nbytes % JSXDR_ALIGN; if (padlen) { padlen = JSXDR_ALIGN - padlen; nbytes += padlen; } if (!(raw = (jschar *) xdr->ops->raw(xdr, nbytes))) return JS_FALSE; if (xdr->mode == JSXDR_ENCODE) { for (i = 0; i != nchars; i++) raw[i] = JSXDR_SWAB16(chars[i]); if (padlen) memset((char *)raw + nbytes - padlen, 0, padlen); } else if (xdr->mode == JSXDR_DECODE) { for (i = 0; i != nchars; i++) chars[i] = JSXDR_SWAB16(raw[i]); } return JS_TRUE; } /* * Convert between a JS (Unicode) string and the XDR representation. */ JS_PUBLIC_API(JSBool) JS_XDRString(JSXDRState *xdr, JSString **strp) { uint32 nchars; jschar *chars; if (xdr->mode == JSXDR_ENCODE) nchars = JSSTRING_LENGTH(*strp); if (!JS_XDRUint32(xdr, &nchars)) return JS_FALSE; if (xdr->mode == JSXDR_DECODE) { chars = (jschar *) JS_malloc(xdr->cx, (nchars + 1) * sizeof(jschar)); if (!chars) return JS_FALSE; } else { chars = JSSTRING_CHARS(*strp); } if (!XDRChars(xdr, chars, nchars)) goto bad; if (xdr->mode == JSXDR_DECODE) { chars[nchars] = 0; *strp = JS_NewUCString(xdr->cx, chars, nchars); if (!*strp) goto bad; } return JS_TRUE; bad: if (xdr->mode == JSXDR_DECODE) JS_free(xdr->cx, chars); return JS_FALSE; } JS_PUBLIC_API(JSBool) JS_XDRStringOrNull(JSXDRState *xdr, JSString **strp) { uint32 null = (*strp == NULL); if (!JS_XDRUint32(xdr, &null)) return JS_FALSE; if (null) { *strp = NULL; return JS_TRUE; } return JS_XDRString(xdr, strp); } static JSBool XDRDoubleValue(JSXDRState *xdr, jsdouble *dp) { jsdpun u; if (xdr->mode == JSXDR_ENCODE) u.d = *dp; if (!JS_XDRUint32(xdr, &u.s.lo) || !JS_XDRUint32(xdr, &u.s.hi)) return JS_FALSE; if (xdr->mode == JSXDR_DECODE) *dp = u.d; return JS_TRUE; } JS_PUBLIC_API(JSBool) JS_XDRDouble(JSXDRState *xdr, jsdouble **dpp) { jsdouble d; if (xdr->mode == JSXDR_ENCODE) d = **dpp; if (!XDRDoubleValue(xdr, &d)) return JS_FALSE; if (xdr->mode == JSXDR_DECODE) { *dpp = JS_NewDouble(xdr->cx, d); if (!*dpp) return JS_FALSE; } return JS_TRUE; } /* These are magic pseudo-tags: see jsapi.h, near the top, for real tags. */ #define JSVAL_XDRNULL 0x8 #define JSVAL_XDRVOID 0xA static JSBool XDRValueBody(JSXDRState *xdr, uint32 type, jsval *vp) { switch (type) { case JSVAL_XDRNULL: *vp = JSVAL_NULL; break; case JSVAL_XDRVOID: *vp = JSVAL_VOID; break; case JSVAL_STRING: { JSString *str; if (xdr->mode == JSXDR_ENCODE) str = JSVAL_TO_STRING(*vp); if (!JS_XDRString(xdr, &str)) return JS_FALSE; if (xdr->mode == JSXDR_DECODE) *vp = STRING_TO_JSVAL(str); break; } case JSVAL_DOUBLE: { jsdouble *dp; if (xdr->mode == JSXDR_ENCODE) dp = JSVAL_TO_DOUBLE(*vp); if (!JS_XDRDouble(xdr, &dp)) return JS_FALSE; if (xdr->mode == JSXDR_DECODE) *vp = DOUBLE_TO_JSVAL(dp); break; } case JSVAL_OBJECT: { JSObject *obj; if (xdr->mode == JSXDR_ENCODE) obj = JSVAL_TO_OBJECT(*vp); if (!js_XDRObject(xdr, &obj)) return JS_FALSE; if (xdr->mode == JSXDR_DECODE) *vp = OBJECT_TO_JSVAL(obj); break; } case JSVAL_BOOLEAN: { uint32 b; if (xdr->mode == JSXDR_ENCODE) b = (uint32) JSVAL_TO_BOOLEAN(*vp); if (!JS_XDRUint32(xdr, &b)) return JS_FALSE; if (xdr->mode == JSXDR_DECODE) *vp = BOOLEAN_TO_JSVAL((JSBool) b); break; } default: { uint32 i; JS_ASSERT(type & JSVAL_INT); if (xdr->mode == JSXDR_ENCODE) i = (uint32) JSVAL_TO_INT(*vp); if (!JS_XDRUint32(xdr, &i)) return JS_FALSE; if (xdr->mode == JSXDR_DECODE) *vp = INT_TO_JSVAL((int32) i); break; } } return JS_TRUE; } JS_PUBLIC_API(JSBool) JS_XDRValue(JSXDRState *xdr, jsval *vp) { uint32 type; if (xdr->mode == JSXDR_ENCODE) { if (JSVAL_IS_NULL(*vp)) type = JSVAL_XDRNULL; else if (JSVAL_IS_VOID(*vp)) type = JSVAL_XDRVOID; else type = JSVAL_TAG(*vp); } return JS_XDRUint32(xdr, &type) && XDRValueBody(xdr, type, vp); } JSBool js_XDRAtom(JSXDRState *xdr, JSAtom **atomp) { jsval v; uint32 type; jsdouble d; JSAtom *atom; if (xdr->mode == JSXDR_ENCODE) { v = ATOM_KEY(*atomp); return JS_XDRValue(xdr, &v); } /* * Inline JS_XDRValue when decoding to avoid ceation of GC things when * then corresponding atom already exists. See bug 321985. */ if (!JS_XDRUint32(xdr, &type)) return JS_FALSE; if (type == JSVAL_STRING) return js_XDRStringAtom(xdr, atomp); if (type == JSVAL_DOUBLE) { if (!XDRDoubleValue(xdr, &d)) return JS_FALSE; atom = js_AtomizeDouble(xdr->cx, d, 0); } else { if (!XDRValueBody(xdr, type, &v)) return JS_FALSE; atom = js_AtomizeValue(xdr->cx, v, 0); } if (!atom) return JS_FALSE; *atomp = atom; return JS_TRUE; } extern JSBool js_XDRStringAtom(JSXDRState *xdr, JSAtom **atomp) { JSString *str; uint32 nchars; JSAtom *atom; JSContext *cx; void *mark; jschar *chars; if (xdr->mode == JSXDR_ENCODE) { JS_ASSERT(ATOM_IS_STRING(*atomp)); str = ATOM_TO_STRING(*atomp); return JS_XDRString(xdr, &str); } /* * Inline JS_XDRString when decoding to avoid JSString allocation * for already existing atoms. See bug 321985. */ if (!JS_XDRUint32(xdr, &nchars)) return JS_FALSE; atom = NULL; cx = xdr->cx; mark = JS_ARENA_MARK(&cx->tempPool); JS_ARENA_ALLOCATE_CAST(chars, jschar *, &cx->tempPool, nchars * sizeof(jschar)); if (!chars) JS_ReportOutOfMemory(cx); else if (XDRChars(xdr, chars, nchars)) atom = js_AtomizeChars(cx, chars, nchars, 0); JS_ARENA_RELEASE(&cx->tempPool, mark); if (!atom) return JS_FALSE; *atomp = atom; return JS_TRUE; } /* * FIXME: This performs lossy conversion and we need to switch to * js_XDRStringAtom while allowing to read older XDR files. See bug 325202. */ JSBool js_XDRCStringAtom(JSXDRState *xdr, JSAtom **atomp) { char *bytes; uint32 nbytes; JSAtom *atom; JSContext *cx; void *mark; if (xdr->mode == JSXDR_ENCODE) { JS_ASSERT(ATOM_IS_STRING(*atomp)); bytes = JS_GetStringBytes(ATOM_TO_STRING(*atomp)); return JS_XDRCString(xdr, &bytes); } /* * Inline JS_XDRCString when decoding not to malloc temporary buffer * just to free it after atomization. See bug 321985. */ if (!JS_XDRUint32(xdr, &nbytes)) return JS_FALSE; atom = NULL; cx = xdr->cx; mark = JS_ARENA_MARK(&cx->tempPool); JS_ARENA_ALLOCATE_CAST(bytes, char *, &cx->tempPool, nbytes * sizeof *bytes); if (!bytes) JS_ReportOutOfMemory(cx); else if (JS_XDRBytes(xdr, bytes, nbytes)) atom = js_Atomize(cx, bytes, nbytes, 0); JS_ARENA_RELEASE(&cx->tempPool, mark); if (!atom) return JS_FALSE; *atomp = atom; return JS_TRUE; } JS_PUBLIC_API(JSBool) JS_XDRScript(JSXDRState *xdr, JSScript **scriptp) { if (!js_XDRScript(xdr, scriptp, NULL)) return JS_FALSE; if (xdr->mode == JSXDR_DECODE) js_CallNewScriptHook(xdr->cx, *scriptp, NULL); return JS_TRUE; } #define CLASS_REGISTRY_MIN 8 #define CLASS_INDEX_TO_ID(i) ((i)+1) #define CLASS_ID_TO_INDEX(id) ((id)-1) typedef struct JSRegHashEntry { JSDHashEntryHdr hdr; const char *name; uint32 index; } JSRegHashEntry; JS_PUBLIC_API(JSBool) JS_XDRRegisterClass(JSXDRState *xdr, JSClass *clasp, uint32 *idp) { uintN numclasses, maxclasses; JSClass **registry; numclasses = xdr->numclasses; maxclasses = xdr->maxclasses; if (numclasses == maxclasses) { maxclasses = (maxclasses == 0) ? CLASS_REGISTRY_MIN : maxclasses << 1; registry = (JSClass **) JS_realloc(xdr->cx, xdr->registry, maxclasses * sizeof(JSClass *)); if (!registry) return JS_FALSE; xdr->registry = registry; xdr->maxclasses = maxclasses; } else { JS_ASSERT(numclasses && numclasses < maxclasses); registry = xdr->registry; } registry[numclasses] = clasp; if (xdr->reghash) { JSRegHashEntry *entry = (JSRegHashEntry *) JS_DHashTableOperate(xdr->reghash, clasp->name, JS_DHASH_ADD); if (!entry) { JS_ReportOutOfMemory(xdr->cx); return JS_FALSE; } entry->name = clasp->name; entry->index = numclasses; } *idp = CLASS_INDEX_TO_ID(numclasses); xdr->numclasses = ++numclasses; return JS_TRUE; } JS_PUBLIC_API(uint32) JS_XDRFindClassIdByName(JSXDRState *xdr, const char *name) { uintN i, numclasses; numclasses = xdr->numclasses; if (numclasses >= 10) { JSRegHashEntry *entry; /* Bootstrap reghash from registry on first overpopulated Find. */ if (!xdr->reghash) { xdr->reghash = JS_NewDHashTable(JS_DHashGetStubOps(), NULL, sizeof(JSRegHashEntry), numclasses); if (xdr->reghash) { for (i = 0; i < numclasses; i++) { JSClass *clasp = xdr->registry[i]; entry = (JSRegHashEntry *) JS_DHashTableOperate(xdr->reghash, clasp->name, JS_DHASH_ADD); entry->name = clasp->name; entry->index = i; } } } /* If we managed to create reghash, use it for O(1) Find. */ if (xdr->reghash) { entry = (JSRegHashEntry *) JS_DHashTableOperate(xdr->reghash, name, JS_DHASH_LOOKUP); if (JS_DHASH_ENTRY_IS_BUSY(&entry->hdr)) return CLASS_INDEX_TO_ID(entry->index); } } /* Only a few classes, or we couldn't malloc reghash: use linear search. */ for (i = 0; i < numclasses; i++) { if (!strcmp(name, xdr->registry[i]->name)) return CLASS_INDEX_TO_ID(i); } return 0; } JS_PUBLIC_API(JSClass *) JS_XDRFindClassById(JSXDRState *xdr, uint32 id) { uintN i = CLASS_ID_TO_INDEX(id); if (i >= xdr->numclasses) return NULL; return xdr->registry[i]; } #endif /* JS_HAS_XDR */ pacparser-1.4.5/src/spidermonkey/js/src/jsxdrapi.h000066400000000000000000000160551464010763600222170ustar00rootroot00000000000000/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- * vim: set ts=8 sw=4 et tw=78: * * ***** BEGIN LICENSE BLOCK ***** * Version: MPL 1.1/GPL 2.0/LGPL 2.1 * * The contents of this file are subject to the Mozilla Public License Version * 1.1 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * http://www.mozilla.org/MPL/ * * Software distributed under the License is distributed on an "AS IS" basis, * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License * for the specific language governing rights and limitations under the * License. * * The Original Code is Mozilla Communicator client code, released * March 31, 1998. * * The Initial Developer of the Original Code is * Netscape Communications Corporation. * Portions created by the Initial Developer are Copyright (C) 1998 * the Initial Developer. All Rights Reserved. * * Contributor(s): * * Alternatively, the contents of this file may be used under the terms of * either of the GNU General Public License Version 2 or later (the "GPL"), * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), * in which case the provisions of the GPL or the LGPL are applicable instead * of those above. If you wish to allow use of your version of this file only * under the terms of either the GPL or the LGPL, and not to allow others to * use your version of this file under the terms of the MPL, indicate your * decision by deleting the provisions above and replace them with the notice * and other provisions required by the GPL or the LGPL. If you do not delete * the provisions above, a recipient may use your version of this file under * the terms of any one of the MPL, the GPL or the LGPL. * * ***** END LICENSE BLOCK ***** */ #ifndef jsxdrapi_h___ #define jsxdrapi_h___ /* * JS external data representation interface API. * * The XDR system is comprised of three major parts: * * - the state serialization/deserialization APIs, which allow consumers * of the API to serialize JS runtime state (script bytecodes, atom maps, * object graphs, etc.) for later restoration. These portions * are implemented in various appropriate files, such as jsscript.c * for the script portions and jsobj.c for object state. * - the callback APIs through which the runtime requests an opaque * representation of a native object, and through which the runtime * constructs a live native object from an opaque representation. These * portions are the responsibility of the native object implementor. * - utility functions for en/decoding of primitive types, such as * JSStrings. This portion is implemented in jsxdrapi.c. * * Spiritually guided by Sun's XDR, where appropriate. */ #include "jspubtd.h" #include "jsprvtd.h" JS_BEGIN_EXTERN_C /* We use little-endian byteorder for all encoded data */ #if defined IS_LITTLE_ENDIAN #define JSXDR_SWAB32(x) x #define JSXDR_SWAB16(x) x #elif defined IS_BIG_ENDIAN #define JSXDR_SWAB32(x) (((uint32)(x) >> 24) | \ (((uint32)(x) >> 8) & 0xff00) | \ (((uint32)(x) << 8) & 0xff0000) | \ ((uint32)(x) << 24)) #define JSXDR_SWAB16(x) (((uint16)(x) >> 8) | ((uint16)(x) << 8)) #else #error "unknown byte order" #endif #define JSXDR_ALIGN 4 typedef enum JSXDRMode { JSXDR_ENCODE, JSXDR_DECODE, JSXDR_FREE } JSXDRMode; typedef enum JSXDRWhence { JSXDR_SEEK_SET, JSXDR_SEEK_CUR, JSXDR_SEEK_END } JSXDRWhence; typedef struct JSXDROps { JSBool (*get32)(JSXDRState *, uint32 *); JSBool (*set32)(JSXDRState *, uint32 *); JSBool (*getbytes)(JSXDRState *, char *, uint32); JSBool (*setbytes)(JSXDRState *, char *, uint32); void * (*raw)(JSXDRState *, uint32); JSBool (*seek)(JSXDRState *, int32, JSXDRWhence); uint32 (*tell)(JSXDRState *); void (*finalize)(JSXDRState *); } JSXDROps; struct JSXDRState { JSXDRMode mode; JSXDROps *ops; JSContext *cx; JSClass **registry; uintN numclasses; uintN maxclasses; void *reghash; void *userdata; JSScript *script; }; extern JS_PUBLIC_API(void) JS_XDRInitBase(JSXDRState *xdr, JSXDRMode mode, JSContext *cx); extern JS_PUBLIC_API(JSXDRState *) JS_XDRNewMem(JSContext *cx, JSXDRMode mode); extern JS_PUBLIC_API(void *) JS_XDRMemGetData(JSXDRState *xdr, uint32 *lp); extern JS_PUBLIC_API(void) JS_XDRMemSetData(JSXDRState *xdr, void *data, uint32 len); extern JS_PUBLIC_API(uint32) JS_XDRMemDataLeft(JSXDRState *xdr); extern JS_PUBLIC_API(void) JS_XDRMemResetData(JSXDRState *xdr); extern JS_PUBLIC_API(void) JS_XDRDestroy(JSXDRState *xdr); extern JS_PUBLIC_API(JSBool) JS_XDRUint8(JSXDRState *xdr, uint8 *b); extern JS_PUBLIC_API(JSBool) JS_XDRUint16(JSXDRState *xdr, uint16 *s); extern JS_PUBLIC_API(JSBool) JS_XDRUint32(JSXDRState *xdr, uint32 *lp); extern JS_PUBLIC_API(JSBool) JS_XDRBytes(JSXDRState *xdr, char *bytes, uint32 len); extern JS_PUBLIC_API(JSBool) JS_XDRCString(JSXDRState *xdr, char **sp); extern JS_PUBLIC_API(JSBool) JS_XDRCStringOrNull(JSXDRState *xdr, char **sp); extern JS_PUBLIC_API(JSBool) JS_XDRString(JSXDRState *xdr, JSString **strp); extern JS_PUBLIC_API(JSBool) JS_XDRStringOrNull(JSXDRState *xdr, JSString **strp); extern JS_PUBLIC_API(JSBool) JS_XDRDouble(JSXDRState *xdr, jsdouble **dp); extern JS_PUBLIC_API(JSBool) JS_XDRValue(JSXDRState *xdr, jsval *vp); extern JS_PUBLIC_API(JSBool) JS_XDRScript(JSXDRState *xdr, JSScript **scriptp); extern JS_PUBLIC_API(JSBool) JS_XDRRegisterClass(JSXDRState *xdr, JSClass *clasp, uint32 *lp); extern JS_PUBLIC_API(uint32) JS_XDRFindClassIdByName(JSXDRState *xdr, const char *name); extern JS_PUBLIC_API(JSClass *) JS_XDRFindClassById(JSXDRState *xdr, uint32 id); /* * Magic numbers. */ #define JSXDR_MAGIC_SCRIPT_1 0xdead0001 #define JSXDR_MAGIC_SCRIPT_2 0xdead0002 #define JSXDR_MAGIC_SCRIPT_3 0xdead0003 #define JSXDR_MAGIC_SCRIPT_4 0xdead0004 #define JSXDR_MAGIC_SCRIPT_5 0xdead0005 #define JSXDR_MAGIC_SCRIPT_CURRENT JSXDR_MAGIC_SCRIPT_5 /* * Bytecode version number. Decrement the second term whenever JS bytecode * changes incompatibly. * * This version number should be XDR'ed once near the front of any file or * larger storage unit containing XDR'ed bytecode and other data, and checked * before deserialization of bytecode. If the saved version does not match * the current version, abort deserialization and invalidate the file. */ #define JSXDR_BYTECODE_VERSION (0xb973c0de - 16) /* * Library-private functions. */ extern JSBool js_XDRAtom(JSXDRState *xdr, JSAtom **atomp); extern JSBool js_XDRStringAtom(JSXDRState *xdr, JSAtom **atomp); /* * FIXME: This is non-unicode version of js_XDRStringAtom that performs lossy * conversion. Do not use it in the new code! See bug 325202. */ extern JSBool js_XDRCStringAtom(JSXDRState *xdr, JSAtom **atomp); JS_END_EXTERN_C #endif /* ! jsxdrapi_h___ */ pacparser-1.4.5/src/spidermonkey/js/src/jsxml.c000066400000000000000000007556731464010763600215430ustar00rootroot00000000000000/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- * vim: set ts=4 sw=4 et tw=78: * * ***** BEGIN LICENSE BLOCK ***** * Version: MPL 1.1/GPL 2.0/LGPL 2.1 * * The contents of this file are subject to the Mozilla Public License Version * 1.1 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * http://www.mozilla.org/MPL/ * * Software distributed under the License is distributed on an "AS IS" basis, * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License * for the specific language governing rights and limitations under the * License. * * The Original Code is SpiderMonkey E4X code, released August, 2004. * * The Initial Developer of the Original Code is * Netscape Communications Corporation. * Portions created by the Initial Developer are Copyright (C) 1998 * the Initial Developer. All Rights Reserved. * * Contributor(s): * * Alternatively, the contents of this file may be used under the terms of * either of the GNU General Public License Version 2 or later (the "GPL"), * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), * in which case the provisions of the GPL or the LGPL are applicable instead * of those above. If you wish to allow use of your version of this file only * under the terms of either the GPL or the LGPL, and not to allow others to * use your version of this file under the terms of the MPL, indicate your * decision by deleting the provisions above and replace them with the notice * and other provisions required by the GPL or the LGPL. If you do not delete * the provisions above, a recipient may use your version of this file under * the terms of any one of the MPL, the GPL or the LGPL. * * ***** END LICENSE BLOCK ***** */ #include "jsstddef.h" #include "jsconfig.h" #if JS_HAS_XML_SUPPORT #include #include #include #include "jstypes.h" #include "jsbit.h" #include "jsprf.h" #include "jsutil.h" #include "jsapi.h" #include "jsarray.h" #include "jsatom.h" #include "jsbool.h" #include "jscntxt.h" #include "jsfun.h" #include "jsgc.h" #include "jsinterp.h" #include "jslock.h" #include "jsnum.h" #include "jsobj.h" #include "jsopcode.h" #include "jsparse.h" #include "jsscan.h" #include "jsscope.h" #include "jsscript.h" #include "jsstr.h" #include "jsxml.h" #ifdef DEBUG #include /* for #ifdef DEBUG memset calls */ #endif /* * NOTES * - in the js shell, you must use the -x command line option, or call * options('xml') before compiling anything that uses XML literals * * TODO * - XXXbe patrol * - Fuse objects and their JSXML* private data into single GC-things * - fix function::foo vs. x.(foo == 42) collision using proper namespacing * - fix the !TCF_HAS_DEFXMLNS optimization in js_FoldConstants * - JSCLASS_DOCUMENT_OBSERVER support -- live two-way binding to Gecko's DOM! * - JS_TypeOfValue sure could use a cleaner interface to "types" */ #ifdef DEBUG_brendan #define METERING 1 #endif #ifdef METERING static struct { jsrefcount qname; jsrefcount qnameobj; jsrefcount liveqname; jsrefcount liveqnameobj; jsrefcount namespace; jsrefcount namespaceobj; jsrefcount livenamespace; jsrefcount livenamespaceobj; jsrefcount xml; jsrefcount xmlobj; jsrefcount livexml; jsrefcount livexmlobj; } xml_stats; #define METER(x) JS_ATOMIC_INCREMENT(&(x)) #define UNMETER(x) JS_ATOMIC_DECREMENT(&(x)) #else #define METER(x) /* nothing */ #define UNMETER(x) /* nothing */ #endif /* * Random utilities and global functions. */ const char js_isXMLName_str[] = "isXMLName"; const char js_XMLList_str[] = "XMLList"; const char js_localName_str[] = "localName"; const char js_xml_parent_str[] = "parent"; const char js_prefix_str[] = "prefix"; const char js_toXMLString_str[] = "toXMLString"; const char js_uri_str[] = "uri"; const char js_amp_entity_str[] = "&"; const char js_gt_entity_str[] = ">"; const char js_lt_entity_str[] = "<"; const char js_quot_entity_str[] = """; #define IS_EMPTY(str) (JSSTRING_LENGTH(str) == 0) #define IS_STAR(str) (JSSTRING_LENGTH(str) == 1 && *JSSTRING_CHARS(str) == '*') static JSBool xml_isXMLName(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) { *rval = BOOLEAN_TO_JSVAL(js_IsXMLName(cx, argv[0])); return JS_TRUE; } /* * Namespace class and library functions. */ enum namespace_tinyid { NAMESPACE_PREFIX = -1, NAMESPACE_URI = -2 }; static JSBool namespace_getProperty(JSContext *cx, JSObject *obj, jsval id, jsval *vp) { JSXMLNamespace *ns; if (!JSVAL_IS_INT(id)) return JS_TRUE; ns = (JSXMLNamespace *) JS_GetInstancePrivate(cx, obj, &js_NamespaceClass.base, NULL); if (!ns) return JS_TRUE; switch (JSVAL_TO_INT(id)) { case NAMESPACE_PREFIX: *vp = ns->prefix ? STRING_TO_JSVAL(ns->prefix) : JSVAL_VOID; break; case NAMESPACE_URI: *vp = STRING_TO_JSVAL(ns->uri); break; } return JS_TRUE; } static void namespace_finalize(JSContext *cx, JSObject *obj) { JSXMLNamespace *ns; JSRuntime *rt; ns = (JSXMLNamespace *) JS_GetPrivate(cx, obj); if (!ns) return; JS_ASSERT(ns->object == obj); ns->object = NULL; UNMETER(xml_stats.livenamespaceobj); rt = cx->runtime; if (rt->functionNamespaceObject == obj) rt->functionNamespaceObject = NULL; } static void namespace_mark_vector(JSContext *cx, JSXMLNamespace **vec, uint32 len) { uint32 i; JSXMLNamespace *ns; for (i = 0; i < len; i++) { ns = vec[i]; { #ifdef GC_MARK_DEBUG char buf[100]; JS_snprintf(buf, sizeof buf, "%s=%s", ns->prefix ? JS_GetStringBytes(ns->prefix) : "", JS_GetStringBytes(ns->uri)); #endif GC_MARK(cx, ns, buf); } } } static uint32 namespace_mark(JSContext *cx, JSObject *obj, void *arg) { JSXMLNamespace *ns; ns = (JSXMLNamespace *) JS_GetPrivate(cx, obj); GC_MARK(cx, ns, "private"); return 0; } static JSBool namespace_equality(JSContext *cx, JSObject *obj, jsval v, JSBool *bp) { JSXMLNamespace *ns, *ns2; JSObject *obj2; ns = (JSXMLNamespace *) JS_GetPrivate(cx, obj); JS_ASSERT(JSVAL_IS_OBJECT(v)); obj2 = JSVAL_TO_OBJECT(v); if (!obj2 || OBJ_GET_CLASS(cx, obj2) != &js_NamespaceClass.base) { *bp = JS_FALSE; } else { ns2 = (JSXMLNamespace *) JS_GetPrivate(cx, obj2); *bp = js_EqualStrings(ns->uri, ns2->uri); } return JS_TRUE; } JS_FRIEND_DATA(JSExtendedClass) js_NamespaceClass = { { "Namespace", JSCLASS_HAS_PRIVATE | JSCLASS_CONSTRUCT_PROTOTYPE | JSCLASS_IS_EXTENDED | JSCLASS_HAS_CACHED_PROTO(JSProto_Namespace), JS_PropertyStub, JS_PropertyStub, namespace_getProperty, NULL, JS_EnumerateStub, JS_ResolveStub, JS_ConvertStub, namespace_finalize, NULL, NULL, NULL, NULL, NULL, NULL, namespace_mark, NULL }, namespace_equality,NULL, NULL, NULL, NULL, NULL, NULL, NULL }; #define NAMESPACE_ATTRS \ (JSPROP_ENUMERATE | JSPROP_READONLY | JSPROP_PERMANENT | JSPROP_SHARED) static JSPropertySpec namespace_props[] = { {js_prefix_str, NAMESPACE_PREFIX, NAMESPACE_ATTRS, 0, 0}, {js_uri_str, NAMESPACE_URI, NAMESPACE_ATTRS, 0, 0}, {0,0,0,0,0} }; static JSBool namespace_toString(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) { JSXMLNamespace *ns; ns = (JSXMLNamespace *) JS_GetInstancePrivate(cx, obj, &js_NamespaceClass.base, argv); if (!ns) return JS_FALSE; *rval = STRING_TO_JSVAL(ns->uri); return JS_TRUE; } static JSFunctionSpec namespace_methods[] = { {js_toString_str, namespace_toString, 0,0,0}, {0,0,0,0,0} }; JSXMLNamespace * js_NewXMLNamespace(JSContext *cx, JSString *prefix, JSString *uri, JSBool declared) { JSXMLNamespace *ns; ns = (JSXMLNamespace *) js_NewGCThing(cx, GCX_NAMESPACE, sizeof(JSXMLNamespace)); if (!ns) return NULL; ns->object = NULL; ns->prefix = prefix; ns->uri = uri; ns->declared = declared; METER(xml_stats.namespace); METER(xml_stats.livenamespace); return ns; } void js_MarkXMLNamespace(JSContext *cx, JSXMLNamespace *ns) { GC_MARK(cx, ns->object, "object"); GC_MARK(cx, ns->prefix, "prefix"); GC_MARK(cx, ns->uri, "uri"); } void js_FinalizeXMLNamespace(JSContext *cx, JSXMLNamespace *ns) { UNMETER(xml_stats.livenamespace); } JSObject * js_NewXMLNamespaceObject(JSContext *cx, JSString *prefix, JSString *uri, JSBool declared) { JSXMLNamespace *ns; ns = js_NewXMLNamespace(cx, prefix, uri, declared); if (!ns) return NULL; return js_GetXMLNamespaceObject(cx, ns); } JSObject * js_GetXMLNamespaceObject(JSContext *cx, JSXMLNamespace *ns) { JSObject *obj; obj = ns->object; if (obj) { JS_ASSERT(JS_GetPrivate(cx, obj) == ns); return obj; } obj = js_NewObject(cx, &js_NamespaceClass.base, NULL, NULL); if (!obj || !JS_SetPrivate(cx, obj, ns)) { cx->weakRoots.newborn[GCX_OBJECT] = NULL; return NULL; } ns->object = obj; METER(xml_stats.namespaceobj); METER(xml_stats.livenamespaceobj); return obj; } /* * QName class and library functions. */ enum qname_tinyid { QNAME_URI = -1, QNAME_LOCALNAME = -2 }; static JSBool qname_getProperty(JSContext *cx, JSObject *obj, jsval id, jsval *vp) { JSXMLQName *qn; if (!JSVAL_IS_INT(id)) return JS_TRUE; qn = (JSXMLQName *) JS_GetInstancePrivate(cx, obj, &js_QNameClass.base, NULL); if (!qn) return JS_TRUE; switch (JSVAL_TO_INT(id)) { case QNAME_URI: *vp = qn->uri ? STRING_TO_JSVAL(qn->uri) : JSVAL_NULL; break; case QNAME_LOCALNAME: *vp = STRING_TO_JSVAL(qn->localName); break; } return JS_TRUE; } static void qname_finalize(JSContext *cx, JSObject *obj) { JSXMLQName *qn; qn = (JSXMLQName *) JS_GetPrivate(cx, obj); if (!qn) return; JS_ASSERT(qn->object == obj); qn->object = NULL; UNMETER(xml_stats.liveqnameobj); } static void anyname_finalize(JSContext* cx, JSObject* obj) { JSRuntime *rt; /* Make sure the next call to js_GetAnyName doesn't try to use obj. */ rt = cx->runtime; if (rt->anynameObject == obj) rt->anynameObject = NULL; qname_finalize(cx, obj); } static uint32 qname_mark(JSContext *cx, JSObject *obj, void *arg) { JSXMLQName *qn; qn = (JSXMLQName *) JS_GetPrivate(cx, obj); GC_MARK(cx, qn, "private"); return 0; } static JSBool qname_identity(JSXMLQName *qna, JSXMLQName *qnb) { if (!qna->uri ^ !qnb->uri) return JS_FALSE; if (qna->uri && !js_EqualStrings(qna->uri, qnb->uri)) return JS_FALSE; return js_EqualStrings(qna->localName, qnb->localName); } static JSBool qname_equality(JSContext *cx, JSObject *obj, jsval v, JSBool *bp) { JSXMLQName *qn, *qn2; JSObject *obj2; qn = (JSXMLQName *) JS_GetPrivate(cx, obj); JS_ASSERT(JSVAL_IS_OBJECT(v)); obj2 = JSVAL_TO_OBJECT(v); if (!obj2 || OBJ_GET_CLASS(cx, obj2) != &js_QNameClass.base) { *bp = JS_FALSE; } else { qn2 = (JSXMLQName *) JS_GetPrivate(cx, obj2); *bp = qname_identity(qn, qn2); } return JS_TRUE; } JS_FRIEND_DATA(JSExtendedClass) js_QNameClass = { { "QName", JSCLASS_HAS_PRIVATE | JSCLASS_CONSTRUCT_PROTOTYPE | JSCLASS_IS_EXTENDED | JSCLASS_HAS_CACHED_PROTO(JSProto_QName), JS_PropertyStub, JS_PropertyStub, qname_getProperty, NULL, JS_EnumerateStub, JS_ResolveStub, JS_ConvertStub, qname_finalize, NULL, NULL, NULL, NULL, NULL, NULL, qname_mark, NULL }, qname_equality, NULL, NULL, NULL, NULL, NULL, NULL, NULL }; /* * Classes for the ECMA-357-internal types AttributeName and AnyName, which * are like QName, except that they have no property getters. They share the * qname_toString method, and therefore are exposed as constructable objects * in this implementation. */ JS_FRIEND_DATA(JSClass) js_AttributeNameClass = { js_AttributeName_str, JSCLASS_HAS_PRIVATE | JSCLASS_CONSTRUCT_PROTOTYPE | JSCLASS_HAS_CACHED_PROTO(JSProto_AttributeName), JS_PropertyStub, JS_PropertyStub, JS_PropertyStub, JS_PropertyStub, JS_EnumerateStub, JS_ResolveStub, JS_ConvertStub, qname_finalize, NULL, NULL, NULL, NULL, NULL, NULL, qname_mark, NULL }; JS_FRIEND_DATA(JSClass) js_AnyNameClass = { js_AnyName_str, JSCLASS_HAS_PRIVATE | JSCLASS_CONSTRUCT_PROTOTYPE | JSCLASS_HAS_CACHED_PROTO(JSProto_AnyName), JS_PropertyStub, JS_PropertyStub, JS_PropertyStub, JS_PropertyStub, JS_EnumerateStub, JS_ResolveStub, JS_ConvertStub, anyname_finalize, NULL, NULL, NULL, NULL, NULL, NULL, qname_mark, NULL }; #define QNAME_ATTRS \ (JSPROP_ENUMERATE | JSPROP_READONLY | JSPROP_PERMANENT | JSPROP_SHARED) static JSPropertySpec qname_props[] = { {js_uri_str, QNAME_URI, QNAME_ATTRS, 0, 0}, {js_localName_str, QNAME_LOCALNAME, QNAME_ATTRS, 0, 0}, {0,0,0,0,0} }; static JSBool qname_toString(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) { JSClass *clasp; JSXMLQName *qn; JSString *str, *qualstr; size_t length; jschar *chars; clasp = OBJ_GET_CLASS(cx, obj); if (clasp == &js_AttributeNameClass || clasp == &js_AnyNameClass) { qn = (JSXMLQName *) JS_GetPrivate(cx, obj); } else { qn = (JSXMLQName *) JS_GetInstancePrivate(cx, obj, &js_QNameClass.base, argv); if (!qn) return JS_FALSE; } if (!qn->uri) { /* No uri means wildcard qualifier. */ str = ATOM_TO_STRING(cx->runtime->atomState.starQualifierAtom); } else if (IS_EMPTY(qn->uri)) { /* Empty string for uri means localName is in no namespace. */ str = cx->runtime->emptyString; } else { qualstr = ATOM_TO_STRING(cx->runtime->atomState.qualifierAtom); str = js_ConcatStrings(cx, qn->uri, qualstr); if (!str) return JS_FALSE; } str = js_ConcatStrings(cx, str, qn->localName); if (!str) return JS_FALSE; if (str && clasp == &js_AttributeNameClass) { length = JSSTRING_LENGTH(str); chars = (jschar *) JS_malloc(cx, (length + 2) * sizeof(jschar)); if (!chars) return JS_FALSE; *chars = '@'; js_strncpy(chars + 1, JSSTRING_CHARS(str), length); chars[++length] = 0; str = js_NewString(cx, chars, length, 0); if (!str) { JS_free(cx, chars); return JS_FALSE; } } *rval = STRING_TO_JSVAL(str); return JS_TRUE; } static JSFunctionSpec qname_methods[] = { {js_toString_str, qname_toString, 0,0,0}, {0,0,0,0,0} }; JSXMLQName * js_NewXMLQName(JSContext *cx, JSString *uri, JSString *prefix, JSString *localName) { JSXMLQName *qn; qn = (JSXMLQName *) js_NewGCThing(cx, GCX_QNAME, sizeof(JSXMLQName)); if (!qn) return NULL; qn->object = NULL; qn->uri = uri; qn->prefix = prefix; qn->localName = localName; METER(xml_stats.qname); METER(xml_stats.liveqname); return qn; } void js_MarkXMLQName(JSContext *cx, JSXMLQName *qn) { GC_MARK(cx, qn->object, "object"); GC_MARK(cx, qn->uri, "uri"); GC_MARK(cx, qn->prefix, "prefix"); GC_MARK(cx, qn->localName, "localName"); } void js_FinalizeXMLQName(JSContext *cx, JSXMLQName *qn) { UNMETER(xml_stats.liveqname); } JSObject * js_NewXMLQNameObject(JSContext *cx, JSString *uri, JSString *prefix, JSString *localName) { JSXMLQName *qn; qn = js_NewXMLQName(cx, uri, prefix, localName); if (!qn) return NULL; return js_GetXMLQNameObject(cx, qn); } JSObject * js_GetXMLQNameObject(JSContext *cx, JSXMLQName *qn) { JSObject *obj; obj = qn->object; if (obj) { JS_ASSERT(JS_GetPrivate(cx, obj) == qn); return obj; } obj = js_NewObject(cx, &js_QNameClass.base, NULL, NULL); if (!obj || !JS_SetPrivate(cx, obj, qn)) { cx->weakRoots.newborn[GCX_OBJECT] = NULL; return NULL; } qn->object = obj; METER(xml_stats.qnameobj); METER(xml_stats.liveqnameobj); return obj; } JSObject * js_GetAttributeNameObject(JSContext *cx, JSXMLQName *qn) { JSObject *obj; obj = qn->object; if (obj) { if (OBJ_GET_CLASS(cx, obj) == &js_AttributeNameClass) return obj; qn = js_NewXMLQName(cx, qn->uri, qn->prefix, qn->localName); if (!qn) return NULL; } obj = js_NewObject(cx, &js_AttributeNameClass, NULL, NULL); if (!obj || !JS_SetPrivate(cx, obj, qn)) { cx->weakRoots.newborn[GCX_OBJECT] = NULL; return NULL; } qn->object = obj; METER(xml_stats.qnameobj); METER(xml_stats.liveqnameobj); return obj; } JSObject * js_ConstructXMLQNameObject(JSContext *cx, jsval nsval, jsval lnval) { jsval argv[2]; /* * ECMA-357 11.1.2, * The _QualifiedIdentifier : PropertySelector :: PropertySelector_ * production, step 2. */ if (!JSVAL_IS_PRIMITIVE(nsval) && OBJ_GET_CLASS(cx, JSVAL_TO_OBJECT(nsval)) == &js_AnyNameClass) { nsval = JSVAL_NULL; } argv[0] = nsval; argv[1] = lnval; return js_ConstructObject(cx, &js_QNameClass.base, NULL, NULL, 2, argv); } static JSBool IsXMLName(const jschar *cp, size_t n) { JSBool rv; jschar c; rv = JS_FALSE; if (n != 0 && JS_ISXMLNSSTART(*cp)) { while (--n != 0) { c = *++cp; if (!JS_ISXMLNS(c)) return rv; } rv = JS_TRUE; } return rv; } JSBool js_IsXMLName(JSContext *cx, jsval v) { JSClass *clasp; JSXMLQName *qn; JSString *name; JSErrorReporter older; /* * Inline specialization of the QName constructor called with v passed as * the only argument, to compute the localName for the constructed qname, * without actually allocating the object or computing its uri and prefix. * See ECMA-357 13.1.2.1 step 1 and 13.3.2. */ if (!JSVAL_IS_PRIMITIVE(v) && (clasp = OBJ_GET_CLASS(cx, JSVAL_TO_OBJECT(v)), clasp == &js_QNameClass.base || clasp == &js_AttributeNameClass || clasp == &js_AnyNameClass)) { qn = (JSXMLQName *) JS_GetPrivate(cx, JSVAL_TO_OBJECT(v)); name = qn->localName; } else { older = JS_SetErrorReporter(cx, NULL); name = js_ValueToString(cx, v); JS_SetErrorReporter(cx, older); if (!name) { JS_ClearPendingException(cx); return JS_FALSE; } } return IsXMLName(JSSTRING_CHARS(name), JSSTRING_LENGTH(name)); } static JSBool Namespace(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) { jsval urival, prefixval; JSObject *uriobj; JSBool isNamespace, isQName; JSClass *clasp; JSString *empty, *prefix; JSXMLNamespace *ns, *ns2; JSXMLQName *qn; urival = argv[argc > 1]; isNamespace = isQName = JS_FALSE; if (!JSVAL_IS_PRIMITIVE(urival)) { uriobj = JSVAL_TO_OBJECT(urival); clasp = OBJ_GET_CLASS(cx, uriobj); isNamespace = (clasp == &js_NamespaceClass.base); isQName = (clasp == &js_QNameClass.base); } #ifdef __GNUC__ /* suppress bogus gcc warnings */ else uriobj = NULL; #endif if (!(cx->fp->flags & JSFRAME_CONSTRUCTING)) { /* Namespace called as function. */ if (argc == 1 && isNamespace) { /* Namespace called with one Namespace argument is identity. */ *rval = urival; return JS_TRUE; } /* Create and return a new QName object exactly as if constructed. */ obj = js_NewObject(cx, &js_NamespaceClass.base, NULL, NULL); if (!obj) return JS_FALSE; *rval = OBJECT_TO_JSVAL(obj); } METER(xml_stats.namespaceobj); METER(xml_stats.livenamespaceobj); /* * Create and connect private data to rooted obj early, so we don't have * to worry about rooting string newborns hanging off of the private data * further below. */ empty = cx->runtime->emptyString; ns = js_NewXMLNamespace(cx, empty, empty, JS_FALSE); if (!ns) return JS_FALSE; if (!JS_SetPrivate(cx, obj, ns)) return JS_FALSE; ns->object = obj; if (argc == 1) { if (isNamespace) { ns2 = (JSXMLNamespace *) JS_GetPrivate(cx, uriobj); ns->uri = ns2->uri; ns->prefix = ns2->prefix; } else if (isQName && (qn = (JSXMLQName *) JS_GetPrivate(cx, uriobj))->uri) { ns->uri = qn->uri; ns->prefix = qn->prefix; } else { ns->uri = js_ValueToString(cx, urival); if (!ns->uri) return JS_FALSE; /* NULL here represents *undefined* in ECMA-357 13.2.2 3(c)iii. */ if (!IS_EMPTY(ns->uri)) ns->prefix = NULL; } } else if (argc == 2) { if (isQName && (qn = (JSXMLQName *) JS_GetPrivate(cx, uriobj))->uri) { ns->uri = qn->uri; } else { ns->uri = js_ValueToString(cx, urival); if (!ns->uri) return JS_FALSE; } prefixval = argv[0]; if (IS_EMPTY(ns->uri)) { if (!JSVAL_IS_VOID(prefixval)) { prefix = js_ValueToString(cx, prefixval); if (!prefix) return JS_FALSE; if (!IS_EMPTY(prefix)) { JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_BAD_XML_NAMESPACE, js_ValueToPrintableString(cx, STRING_TO_JSVAL(prefix))); return JS_FALSE; } } } else if (JSVAL_IS_VOID(prefixval) || !js_IsXMLName(cx, prefixval)) { /* NULL here represents *undefined* in ECMA-357 13.2.2 4(d) etc. */ ns->prefix = NULL; } else { prefix = js_ValueToString(cx, prefixval); if (!prefix) return JS_FALSE; ns->prefix = prefix; } } return JS_TRUE; } static JSBool QName(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) { jsval nameval, nsval; JSBool isQName, isNamespace; JSXMLQName *qn; JSString *uri, *prefix, *name; JSObject *nsobj; JSClass *clasp; JSXMLNamespace *ns; nameval = argv[argc > 1]; isQName = !JSVAL_IS_PRIMITIVE(nameval) && OBJ_GET_CLASS(cx, JSVAL_TO_OBJECT(nameval)) == &js_QNameClass.base; if (!(cx->fp->flags & JSFRAME_CONSTRUCTING)) { /* QName called as function. */ if (argc == 1 && isQName) { /* QName called with one QName argument is identity. */ *rval = nameval; return JS_TRUE; } /* * Create and return a new QName object exactly as if constructed. * Use the constructor's clasp so we can be shared by AttributeName * (see below after this function). */ obj = js_NewObject(cx, JS_ValueToFunction(cx, argv[-2])->clasp, NULL, NULL); if (!obj) return JS_FALSE; *rval = OBJECT_TO_JSVAL(obj); } METER(xml_stats.qnameobj); METER(xml_stats.liveqnameobj); if (isQName) { /* If namespace is not specified and name is a QName, clone it. */ qn = (JSXMLQName *) JS_GetPrivate(cx, JSVAL_TO_OBJECT(nameval)); if (argc == 1) { uri = qn->uri; prefix = qn->prefix; name = qn->localName; goto out; } /* Namespace and qname were passed -- use the qname's localName. */ nameval = STRING_TO_JSVAL(qn->localName); } if (argc == 0) { name = cx->runtime->emptyString; } else { name = js_ValueToString(cx, nameval); if (!name) return JS_FALSE; /* Use argv[1] as a local root for name, even if it was not passed. */ argv[1] = STRING_TO_JSVAL(name); } nsval = argv[0]; if (argc == 1 || JSVAL_IS_VOID(nsval)) { if (IS_STAR(name)) { nsval = JSVAL_NULL; } else { if (!js_GetDefaultXMLNamespace(cx, &nsval)) return JS_FALSE; } } if (JSVAL_IS_NULL(nsval)) { /* NULL prefix represents *undefined* in ECMA-357 13.3.2 5(a). */ uri = prefix = NULL; } else { /* * Inline specialization of the Namespace constructor called with * nsval passed as the only argument, to compute the uri and prefix * for the constructed namespace, without actually allocating the * object or computing other members. See ECMA-357 13.3.2 6(a) and * 13.2.2. */ isNamespace = isQName = JS_FALSE; if (!JSVAL_IS_PRIMITIVE(nsval)) { nsobj = JSVAL_TO_OBJECT(nsval); clasp = OBJ_GET_CLASS(cx, nsobj); isNamespace = (clasp == &js_NamespaceClass.base); isQName = (clasp == &js_QNameClass.base); } #ifdef __GNUC__ /* suppress bogus gcc warnings */ else nsobj = NULL; #endif if (isNamespace) { ns = (JSXMLNamespace *) JS_GetPrivate(cx, nsobj); uri = ns->uri; prefix = ns->prefix; } else if (isQName && (qn = (JSXMLQName *) JS_GetPrivate(cx, nsobj))->uri) { uri = qn->uri; prefix = qn->prefix; } else { uri = js_ValueToString(cx, nsval); if (!uri) return JS_FALSE; argv[0] = STRING_TO_JSVAL(uri); /* local root */ /* NULL here represents *undefined* in ECMA-357 13.2.2 3(c)iii. */ prefix = IS_EMPTY(uri) ? cx->runtime->emptyString : NULL; } } out: qn = js_NewXMLQName(cx, uri, prefix, name); if (!qn) return JS_FALSE; if (!JS_SetPrivate(cx, obj, qn)) return JS_FALSE; qn->object = obj; return JS_TRUE; } static JSBool AttributeName(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) { /* * Since js_AttributeNameClass was initialized, obj will have that as its * class, not js_QNameClass. */ return QName(cx, obj, argc, argv, rval); } /* * XMLArray library functions. */ static JSBool namespace_identity(const void *a, const void *b) { const JSXMLNamespace *nsa = (const JSXMLNamespace *) a; const JSXMLNamespace *nsb = (const JSXMLNamespace *) b; if (nsa->prefix && nsb->prefix) { if (!js_EqualStrings(nsa->prefix, nsb->prefix)) return JS_FALSE; } else { if (nsa->prefix || nsb->prefix) return JS_FALSE; } return js_EqualStrings(nsa->uri, nsb->uri); } static JSBool attr_identity(const void *a, const void *b) { const JSXML *xmla = (const JSXML *) a; const JSXML *xmlb = (const JSXML *) b; return qname_identity(xmla->name, xmlb->name); } static void XMLArrayCursorInit(JSXMLArrayCursor *cursor, JSXMLArray *array) { JSXMLArrayCursor *next; cursor->array = array; cursor->index = 0; next = cursor->next = array->cursors; if (next) next->prevp = &cursor->next; cursor->prevp = &array->cursors; array->cursors = cursor; cursor->root = NULL; } static void XMLArrayCursorFinish(JSXMLArrayCursor *cursor) { JSXMLArrayCursor *next; if (!cursor->array) return; next = cursor->next; if (next) next->prevp = cursor->prevp; *cursor->prevp = next; cursor->array = NULL; } static void * XMLArrayCursorNext(JSXMLArrayCursor *cursor) { JSXMLArray *array; array = cursor->array; if (!array || cursor->index >= array->length) return NULL; return cursor->root = array->vector[cursor->index++]; } static void * XMLArrayCursorItem(JSXMLArrayCursor *cursor) { JSXMLArray *array; array = cursor->array; if (!array || cursor->index >= array->length) return NULL; return cursor->root = array->vector[cursor->index]; } static void XMLArrayCursorMark(JSContext *cx, JSXMLArrayCursor *cursor) { while (cursor) { GC_MARK(cx, cursor->root, "cursor->root"); cursor = cursor->next; } } /* NB: called with null cx from the GC, via xml_mark => XMLArrayTrim. */ static JSBool XMLArraySetCapacity(JSContext *cx, JSXMLArray *array, uint32 capacity) { void **vector; if (capacity == 0) { /* We could let realloc(p, 0) free this, but purify gets confused. */ if (array->vector) free(array->vector); vector = NULL; } else { if ((size_t)capacity > ~(size_t)0 / sizeof(void *) || !(vector = (void **) realloc(array->vector, capacity * sizeof(void *)))) { if (cx) JS_ReportOutOfMemory(cx); return JS_FALSE; } } array->capacity = JSXML_PRESET_CAPACITY | capacity; array->vector = vector; return JS_TRUE; } static void XMLArrayTrim(JSXMLArray *array) { if (array->capacity & JSXML_PRESET_CAPACITY) return; if (array->length < array->capacity) XMLArraySetCapacity(NULL, array, array->length); } static JSBool XMLArrayInit(JSContext *cx, JSXMLArray *array, uint32 capacity) { array->length = array->capacity = 0; array->vector = NULL; array->cursors = NULL; return capacity == 0 || XMLArraySetCapacity(cx, array, capacity); } static void XMLArrayFinish(JSContext *cx, JSXMLArray *array) { JSXMLArrayCursor *cursor; JS_free(cx, array->vector); while ((cursor = array->cursors) != NULL) XMLArrayCursorFinish(cursor); #ifdef DEBUG memset(array, 0xd5, sizeof *array); #endif } #define XML_NOT_FOUND ((uint32) -1) static uint32 XMLArrayFindMember(const JSXMLArray *array, void *elt, JSIdentityOp identity) { void **vector; uint32 i, n; /* The identity op must not reallocate array->vector. */ vector = array->vector; if (identity) { for (i = 0, n = array->length; i < n; i++) { if (identity(vector[i], elt)) return i; } } else { for (i = 0, n = array->length; i < n; i++) { if (vector[i] == elt) return i; } } return XML_NOT_FOUND; } /* * Grow array vector capacity by powers of two to LINEAR_THRESHOLD, and after * that, grow by LINEAR_INCREMENT. Both must be powers of two, and threshold * should be greater than increment. */ #define LINEAR_THRESHOLD 256 #define LINEAR_INCREMENT 32 static JSBool XMLArrayAddMember(JSContext *cx, JSXMLArray *array, uint32 index, void *elt) { uint32 capacity, i; int log2; void **vector; if (index >= array->length) { if (index >= JSXML_CAPACITY(array)) { /* Arrange to clear JSXML_PRESET_CAPACITY from array->capacity. */ capacity = index + 1; if (index >= LINEAR_THRESHOLD) { capacity = JS_ROUNDUP(capacity, LINEAR_INCREMENT); } else { JS_CEILING_LOG2(log2, capacity); capacity = JS_BIT(log2); } if ((size_t)capacity > ~(size_t)0 / sizeof(void *) || !(vector = (void **) realloc(array->vector, capacity * sizeof(void *)))) { JS_ReportOutOfMemory(cx); return JS_FALSE; } array->capacity = capacity; array->vector = vector; for (i = array->length; i < index; i++) vector[i] = NULL; } array->length = index + 1; } array->vector[index] = elt; return JS_TRUE; } static JSBool XMLArrayInsert(JSContext *cx, JSXMLArray *array, uint32 i, uint32 n) { uint32 j; JSXMLArrayCursor *cursor; j = array->length; JS_ASSERT(i <= j); if (!XMLArraySetCapacity(cx, array, j + n)) return JS_FALSE; array->length = j + n; JS_ASSERT(n != (uint32)-1); while (j != i) { --j; array->vector[j + n] = array->vector[j]; } for (cursor = array->cursors; cursor; cursor = cursor->next) { if (cursor->index > i) cursor->index += n; } return JS_TRUE; } static void * XMLArrayDelete(JSContext *cx, JSXMLArray *array, uint32 index, JSBool compress) { uint32 length; void **vector, *elt; JSXMLArrayCursor *cursor; length = array->length; if (index >= length) return NULL; vector = array->vector; elt = vector[index]; if (compress) { while (++index < length) vector[index-1] = vector[index]; array->length = length - 1; array->capacity = JSXML_CAPACITY(array); } else { vector[index] = NULL; } for (cursor = array->cursors; cursor; cursor = cursor->next) { if (cursor->index > index) --cursor->index; } return elt; } static void XMLArrayTruncate(JSContext *cx, JSXMLArray *array, uint32 length) { void **vector; JS_ASSERT(!array->cursors); if (length >= array->length) return; if (length == 0) { if (array->vector) free(array->vector); vector = NULL; } else { vector = realloc(array->vector, length * sizeof(void *)); if (!vector) return; } if (array->length > length) array->length = length; array->capacity = length; array->vector = vector; } #define XMLARRAY_FIND_MEMBER(a,e,f) XMLArrayFindMember(a, (void *)(e), f) #define XMLARRAY_HAS_MEMBER(a,e,f) (XMLArrayFindMember(a, (void *)(e), f) != \ XML_NOT_FOUND) #define XMLARRAY_MEMBER(a,i,t) (((i) < (a)->length) \ ? (t *) (a)->vector[i] \ : NULL) #define XMLARRAY_SET_MEMBER(a,i,e) JS_BEGIN_MACRO \ if ((a)->length <= (i)) \ (a)->length = (i) + 1; \ ((a)->vector[i] = (void *)(e)); \ JS_END_MACRO #define XMLARRAY_ADD_MEMBER(x,a,i,e)XMLArrayAddMember(x, a, i, (void *)(e)) #define XMLARRAY_INSERT(x,a,i,n) XMLArrayInsert(x, a, i, n) #define XMLARRAY_APPEND(x,a,e) XMLARRAY_ADD_MEMBER(x, a, (a)->length, (e)) #define XMLARRAY_DELETE(x,a,i,c,t) ((t *) XMLArrayDelete(x, a, i, c)) #define XMLARRAY_TRUNCATE(x,a,n) XMLArrayTruncate(x, a, n) /* * Define XML setting property strings and constants early, so everyone can * use the same names and their magic numbers (tinyids, flags). */ static const char js_ignoreComments_str[] = "ignoreComments"; static const char js_ignoreProcessingInstructions_str[] = "ignoreProcessingInstructions"; static const char js_ignoreWhitespace_str[] = "ignoreWhitespace"; static const char js_prettyPrinting_str[] = "prettyPrinting"; static const char js_prettyIndent_str[] = "prettyIndent"; /* * NB: These XML static property tinyids must * (a) not collide with the generic negative tinyids at the top of jsfun.c; * (b) index their corresponding xml_static_props array elements. * Don't change 'em! */ enum xml_static_tinyid { XML_IGNORE_COMMENTS, XML_IGNORE_PROCESSING_INSTRUCTIONS, XML_IGNORE_WHITESPACE, XML_PRETTY_PRINTING, XML_PRETTY_INDENT }; static JSBool xml_setting_getter(JSContext *cx, JSObject *obj, jsval id, jsval *vp) { return JS_TRUE; } static JSBool xml_setting_setter(JSContext *cx, JSObject *obj, jsval id, jsval *vp) { JSBool b; uint8 flag; JS_ASSERT(JSVAL_IS_INT(id)); if (!js_ValueToBoolean(cx, *vp, &b)) return JS_FALSE; flag = JS_BIT(JSVAL_TO_INT(id)); if (b) cx->xmlSettingFlags |= flag; else cx->xmlSettingFlags &= ~flag; return JS_TRUE; } static JSPropertySpec xml_static_props[] = { {js_ignoreComments_str, XML_IGNORE_COMMENTS, JSPROP_PERMANENT, xml_setting_getter, xml_setting_setter}, {js_ignoreProcessingInstructions_str, XML_IGNORE_PROCESSING_INSTRUCTIONS, JSPROP_PERMANENT, xml_setting_getter, xml_setting_setter}, {js_ignoreWhitespace_str, XML_IGNORE_WHITESPACE, JSPROP_PERMANENT, xml_setting_getter, xml_setting_setter}, {js_prettyPrinting_str, XML_PRETTY_PRINTING, JSPROP_PERMANENT, xml_setting_getter, xml_setting_setter}, {js_prettyIndent_str, XML_PRETTY_INDENT, JSPROP_PERMANENT, xml_setting_getter, NULL}, {0,0,0,0,0} }; /* Derive cx->xmlSettingFlags bits from xml_static_props tinyids. */ #define XSF_IGNORE_COMMENTS JS_BIT(XML_IGNORE_COMMENTS) #define XSF_IGNORE_PROCESSING_INSTRUCTIONS \ JS_BIT(XML_IGNORE_PROCESSING_INSTRUCTIONS) #define XSF_IGNORE_WHITESPACE JS_BIT(XML_IGNORE_WHITESPACE) #define XSF_PRETTY_PRINTING JS_BIT(XML_PRETTY_PRINTING) #define XSF_CACHE_VALID JS_BIT(XML_PRETTY_INDENT) /* * Extra, unrelated but necessarily disjoint flag used by ParseNodeToXML. * This flag means a couple of things: * * - The top JSXML created for a parse tree must have an object owning it. * * - That the default namespace normally inherited from the temporary * tag that wraps a runtime-concatenated XML source * string must, in the case of a precompiled XML object tree, inherit via * ad-hoc code in ParseNodeToXML. * * Because of the second purpose, we name this flag XSF_PRECOMPILED_ROOT. */ #define XSF_PRECOMPILED_ROOT (XSF_CACHE_VALID << 1) /* Macros for special-casing xml:, xmlns= and xmlns:foo= in ParseNodeToQName. */ #define IS_XML(str) \ (JSSTRING_LENGTH(str) == 3 && IS_XML_CHARS(JSSTRING_CHARS(str))) #define IS_XMLNS(str) \ (JSSTRING_LENGTH(str) == 5 && IS_XMLNS_CHARS(JSSTRING_CHARS(str))) #define IS_XML_CHARS(chars) \ (JS_TOLOWER((chars)[0]) == 'x' && \ JS_TOLOWER((chars)[1]) == 'm' && \ JS_TOLOWER((chars)[2]) == 'l') #define HAS_NS_AFTER_XML(chars) \ (JS_TOLOWER((chars)[3]) == 'n' && \ JS_TOLOWER((chars)[4]) == 's') #define IS_XMLNS_CHARS(chars) \ (IS_XML_CHARS(chars) && HAS_NS_AFTER_XML(chars)) #define STARTS_WITH_XML(chars,length) \ (length >= 3 && IS_XML_CHARS(chars)) static const char xml_namespace_str[] = "http://www.w3.org/XML/1998/namespace"; static const char xmlns_namespace_str[] = "http://www.w3.org/2000/xmlns/"; static JSXMLQName * ParseNodeToQName(JSContext *cx, JSParseNode *pn, JSXMLArray *inScopeNSes, JSBool isAttributeName) { JSString *str, *uri, *prefix, *localName; size_t length, offset; const jschar *start, *limit, *colon; uint32 n; JSXMLNamespace *ns; JS_ASSERT(pn->pn_arity == PN_NULLARY); str = ATOM_TO_STRING(pn->pn_atom); length = JSSTRING_LENGTH(str); start = JSSTRING_CHARS(str); JS_ASSERT(length != 0 && *start != '@'); JS_ASSERT(length != 1 || *start != '*'); uri = cx->runtime->emptyString; limit = start + length; colon = js_strchr_limit(start, ':', limit); if (colon) { offset = PTRDIFF(colon, start, jschar); prefix = js_NewDependentString(cx, str, 0, offset, 0); if (!prefix) return NULL; if (STARTS_WITH_XML(start, offset)) { if (offset == 3) { uri = JS_InternString(cx, xml_namespace_str); if (!uri) return NULL; } else if (offset == 5 && HAS_NS_AFTER_XML(start)) { uri = JS_InternString(cx, xmlns_namespace_str); if (!uri) return NULL; } else { uri = NULL; } } else { uri = NULL; n = inScopeNSes->length; while (n != 0) { --n; ns = XMLARRAY_MEMBER(inScopeNSes, n, JSXMLNamespace); if (ns->prefix && js_EqualStrings(ns->prefix, prefix)) { uri = ns->uri; break; } } } if (!uri) { js_ReportCompileErrorNumber(cx, pn, JSREPORT_PN | JSREPORT_ERROR, JSMSG_BAD_XML_NAMESPACE, js_ValueToPrintableString(cx, STRING_TO_JSVAL(prefix))); return NULL; } localName = js_NewStringCopyN(cx, colon + 1, length - (offset + 1), 0); if (!localName) return NULL; } else { if (isAttributeName) { /* * An unprefixed attribute is not in any namespace, so set prefix * as well as uri to the empty string. */ prefix = uri; } else { /* * Loop from back to front looking for the closest declared default * namespace. */ n = inScopeNSes->length; while (n != 0) { --n; ns = XMLARRAY_MEMBER(inScopeNSes, n, JSXMLNamespace); if (!ns->prefix || IS_EMPTY(ns->prefix)) { uri = ns->uri; break; } } prefix = IS_EMPTY(uri) ? cx->runtime->emptyString : NULL; } localName = str; } return js_NewXMLQName(cx, uri, prefix, localName); } static JSString * ChompXMLWhitespace(JSContext *cx, JSString *str) { size_t length, newlength, offset; const jschar *cp, *start, *end; jschar c; length = JSSTRING_LENGTH(str); for (cp = start = JSSTRING_CHARS(str), end = cp + length; cp < end; cp++) { c = *cp; if (!JS_ISXMLSPACE(c)) break; } while (end > cp) { c = end[-1]; if (!JS_ISXMLSPACE(c)) break; --end; } newlength = PTRDIFF(end, cp, jschar); if (newlength == length) return str; offset = PTRDIFF(cp, start, jschar); return js_NewDependentString(cx, str, offset, newlength, 0); } static JSXML * ParseNodeToXML(JSContext *cx, JSParseNode *pn, JSXMLArray *inScopeNSes, uintN flags) { JSXML *xml, *kid, *attr, *attrj; JSString *str; uint32 length, n, i, j; JSParseNode *pn2, *pn3, *head, **pnp; JSXMLNamespace *ns; JSXMLQName *qn, *attrjqn; JSXMLClass xml_class; int stackDummy; if (!JS_CHECK_STACK_SIZE(cx, stackDummy)) { js_ReportCompileErrorNumber(cx, pn, JSREPORT_PN | JSREPORT_ERROR, JSMSG_OVER_RECURSED); return NULL; } #define PN2X_SKIP_CHILD ((JSXML *) 1) /* * Cases return early to avoid common code that gets an outermost xml's * object, which protects GC-things owned by xml and its descendants from * garbage collection. */ xml = NULL; if (!js_EnterLocalRootScope(cx)) return NULL; switch (pn->pn_type) { case TOK_XMLELEM: length = inScopeNSes->length; pn2 = pn->pn_head; xml = ParseNodeToXML(cx, pn2, inScopeNSes, flags); if (!xml) goto fail; flags &= ~XSF_PRECOMPILED_ROOT; n = pn->pn_count; JS_ASSERT(n >= 2); n -= 2; if (!XMLArraySetCapacity(cx, &xml->xml_kids, n)) goto fail; i = 0; while ((pn2 = pn2->pn_next) != NULL) { if (!pn2->pn_next) { /* Don't append the end tag! */ JS_ASSERT(pn2->pn_type == TOK_XMLETAGO); break; } if ((flags & XSF_IGNORE_WHITESPACE) && n > 1 && pn2->pn_type == TOK_XMLSPACE) { --n; continue; } kid = ParseNodeToXML(cx, pn2, inScopeNSes, flags); if (kid == PN2X_SKIP_CHILD) { --n; continue; } if (!kid) goto fail; /* Store kid in xml right away, to protect it from GC. */ XMLARRAY_SET_MEMBER(&xml->xml_kids, i, kid); kid->parent = xml; ++i; /* XXX where is this documented in an XML spec, or in E4X? */ if ((flags & XSF_IGNORE_WHITESPACE) && n > 1 && kid->xml_class == JSXML_CLASS_TEXT) { str = ChompXMLWhitespace(cx, kid->xml_value); if (!str) goto fail; kid->xml_value = str; } } JS_ASSERT(i == n); if (n < pn->pn_count - 2) XMLArrayTrim(&xml->xml_kids); XMLARRAY_TRUNCATE(cx, inScopeNSes, length); break; case TOK_XMLLIST: xml = js_NewXML(cx, JSXML_CLASS_LIST); if (!xml) goto fail; n = pn->pn_count; if (!XMLArraySetCapacity(cx, &xml->xml_kids, n)) goto fail; i = 0; for (pn2 = pn->pn_head; pn2; pn2 = pn2->pn_next) { /* * Always ignore insignificant whitespace in lists -- we shouldn't * condition this on an XML.ignoreWhitespace setting when the list * constructor is XMLList (note XML/XMLList unification hazard). */ if (pn2->pn_type == TOK_XMLSPACE) { --n; continue; } kid = ParseNodeToXML(cx, pn2, inScopeNSes, flags); if (kid == PN2X_SKIP_CHILD) { --n; continue; } if (!kid) goto fail; XMLARRAY_SET_MEMBER(&xml->xml_kids, i, kid); ++i; } if (n < pn->pn_count) XMLArrayTrim(&xml->xml_kids); break; case TOK_XMLSTAGO: case TOK_XMLPTAGC: length = inScopeNSes->length; pn2 = pn->pn_head; JS_ASSERT(pn2->pn_type == TOK_XMLNAME); if (pn2->pn_arity == PN_LIST) goto syntax; xml = js_NewXML(cx, JSXML_CLASS_ELEMENT); if (!xml) goto fail; /* First pass: check syntax and process namespace declarations. */ JS_ASSERT(pn->pn_count >= 1); n = pn->pn_count - 1; pnp = &pn2->pn_next; head = *pnp; while ((pn2 = *pnp) != NULL) { size_t length; const jschar *chars; if (pn2->pn_type != TOK_XMLNAME || pn2->pn_arity != PN_NULLARY) goto syntax; /* Enforce "Well-formedness constraint: Unique Att Spec". */ for (pn3 = head; pn3 != pn2; pn3 = pn3->pn_next->pn_next) { if (pn3->pn_atom == pn2->pn_atom) { js_ReportCompileErrorNumber(cx, pn2, JSREPORT_PN | JSREPORT_ERROR, JSMSG_DUPLICATE_XML_ATTR, js_ValueToPrintableString(cx, ATOM_KEY(pn2->pn_atom))); goto fail; } } str = ATOM_TO_STRING(pn2->pn_atom); pn2 = pn2->pn_next; JS_ASSERT(pn2); if (pn2->pn_type != TOK_XMLATTR) goto syntax; length = JSSTRING_LENGTH(str); chars = JSSTRING_CHARS(str); if (length >= 5 && IS_XMLNS_CHARS(chars) && (length == 5 || chars[5] == ':')) { JSString *uri, *prefix; uri = ATOM_TO_STRING(pn2->pn_atom); if (length == 5) { /* 10.3.2.1. Step 6(h)(i)(1)(a). */ prefix = cx->runtime->emptyString; } else { prefix = js_NewStringCopyN(cx, chars + 6, length - 6, 0); if (!prefix) goto fail; } /* * Once the new ns is appended to xml->xml_namespaces, it is * protected from GC by the object that owns xml -- which is * either xml->object if outermost, or the object owning xml's * oldest ancestor if !outermost. */ ns = js_NewXMLNamespace(cx, prefix, uri, JS_TRUE); if (!ns) goto fail; /* * Don't add a namespace that's already in scope. If someone * extracts a child property from its parent via [[Get]], then * we enforce the invariant, noted many times in ECMA-357, that * the child's namespaces form a possibly-improper superset of * its ancestors' namespaces. */ if (!XMLARRAY_HAS_MEMBER(inScopeNSes, ns, namespace_identity)) { if (!XMLARRAY_APPEND(cx, inScopeNSes, ns) || !XMLARRAY_APPEND(cx, &xml->xml_namespaces, ns)) { goto fail; } } JS_ASSERT(n >= 2); n -= 2; *pnp = pn2->pn_next; /* XXXbe recycle pn2 */ continue; } pnp = &pn2->pn_next; } /* * If called from js_ParseNodeToXMLObject, emulate the effect of the * ... wrapping done by "ToXML Applied to * the String Type" (ECMA-357 10.3.1). */ if (flags & XSF_PRECOMPILED_ROOT) { JS_ASSERT(length >= 1); ns = XMLARRAY_MEMBER(inScopeNSes, 0, JSXMLNamespace); JS_ASSERT(!XMLARRAY_HAS_MEMBER(&xml->xml_namespaces, ns, namespace_identity)); ns = js_NewXMLNamespace(cx, ns->prefix, ns->uri, JS_FALSE); if (!ns) goto fail; if (!XMLARRAY_APPEND(cx, &xml->xml_namespaces, ns)) goto fail; } XMLArrayTrim(&xml->xml_namespaces); /* Second pass: process tag name and attributes, using namespaces. */ pn2 = pn->pn_head; qn = ParseNodeToQName(cx, pn2, inScopeNSes, JS_FALSE); if (!qn) goto fail; xml->name = qn; JS_ASSERT((n & 1) == 0); n >>= 1; if (!XMLArraySetCapacity(cx, &xml->xml_attrs, n)) goto fail; for (i = 0; (pn2 = pn2->pn_next) != NULL; i++) { qn = ParseNodeToQName(cx, pn2, inScopeNSes, JS_TRUE); if (!qn) { xml->xml_attrs.length = i; goto fail; } /* * Enforce "Well-formedness constraint: Unique Att Spec", part 2: * this time checking local name and namespace URI. */ for (j = 0; j < i; j++) { attrj = XMLARRAY_MEMBER(&xml->xml_attrs, j, JSXML); attrjqn = attrj->name; if (js_EqualStrings(attrjqn->uri, qn->uri) && js_EqualStrings(attrjqn->localName, qn->localName)) { js_ReportCompileErrorNumber(cx, pn2, JSREPORT_PN | JSREPORT_ERROR, JSMSG_DUPLICATE_XML_ATTR, js_ValueToPrintableString(cx, ATOM_KEY(pn2->pn_atom))); goto fail; } } pn2 = pn2->pn_next; JS_ASSERT(pn2); JS_ASSERT(pn2->pn_type == TOK_XMLATTR); attr = js_NewXML(cx, JSXML_CLASS_ATTRIBUTE); if (!attr) goto fail; XMLARRAY_SET_MEMBER(&xml->xml_attrs, i, attr); attr->parent = xml; attr->name = qn; attr->xml_value = ATOM_TO_STRING(pn2->pn_atom); } /* Point tag closes its own namespace scope. */ if (pn->pn_type == TOK_XMLPTAGC) XMLARRAY_TRUNCATE(cx, inScopeNSes, length); break; case TOK_XMLSPACE: case TOK_XMLTEXT: case TOK_XMLCDATA: case TOK_XMLCOMMENT: case TOK_XMLPI: str = ATOM_TO_STRING(pn->pn_atom); qn = NULL; if (pn->pn_type == TOK_XMLCOMMENT) { if (flags & XSF_IGNORE_COMMENTS) goto skip_child; xml_class = JSXML_CLASS_COMMENT; } else if (pn->pn_type == TOK_XMLPI) { if (IS_XML(str)) { js_ReportCompileErrorNumber(cx, pn, JSREPORT_PN | JSREPORT_ERROR, JSMSG_RESERVED_ID, js_ValueToPrintableString(cx, STRING_TO_JSVAL(str))); goto fail; } if (flags & XSF_IGNORE_PROCESSING_INSTRUCTIONS) goto skip_child; qn = ParseNodeToQName(cx, pn, inScopeNSes, JS_FALSE); if (!qn) goto fail; str = pn->pn_atom2 ? ATOM_TO_STRING(pn->pn_atom2) : cx->runtime->emptyString; xml_class = JSXML_CLASS_PROCESSING_INSTRUCTION; } else { /* CDATA section content, or element text. */ xml_class = JSXML_CLASS_TEXT; } xml = js_NewXML(cx, xml_class); if (!xml) goto fail; xml->name = qn; if (pn->pn_type == TOK_XMLSPACE) xml->xml_flags |= XMLF_WHITESPACE_TEXT; xml->xml_value = str; break; default: goto syntax; } js_LeaveLocalRootScopeWithResult(cx, (jsval) xml); if ((flags & XSF_PRECOMPILED_ROOT) && !js_GetXMLObject(cx, xml)) return NULL; return xml; skip_child: js_LeaveLocalRootScope(cx); return PN2X_SKIP_CHILD; #undef PN2X_SKIP_CHILD syntax: js_ReportCompileErrorNumber(cx, pn, JSREPORT_PN | JSREPORT_ERROR, JSMSG_BAD_XML_MARKUP); fail: js_LeaveLocalRootScope(cx); return NULL; } /* * XML helper, object-ops, and library functions. We start with the helpers, * in ECMA-357 order, but merging XML (9.1) and XMLList (9.2) helpers. */ static JSBool GetXMLSetting(JSContext *cx, const char *name, jsval *vp) { jsval v; if (!js_FindClassObject(cx, NULL, INT_TO_JSID(JSProto_XML), &v)) return JS_FALSE; if (!VALUE_IS_FUNCTION(cx, v)) { *vp = JSVAL_VOID; return JS_TRUE; } return JS_GetProperty(cx, JSVAL_TO_OBJECT(v), name, vp); } static JSBool FillSettingsCache(JSContext *cx) { int i; const char *name; jsval v; JSBool isSet; /* Note: XML_PRETTY_INDENT is not a boolean setting. */ for (i = XML_IGNORE_COMMENTS; i < XML_PRETTY_INDENT; i++) { name = xml_static_props[i].name; if (!GetXMLSetting(cx, name, &v) || !js_ValueToBoolean(cx, v, &isSet)) return JS_FALSE; if (isSet) cx->xmlSettingFlags |= JS_BIT(i); else cx->xmlSettingFlags &= ~JS_BIT(i); } cx->xmlSettingFlags |= XSF_CACHE_VALID; return JS_TRUE; } static JSBool GetBooleanXMLSetting(JSContext *cx, const char *name, JSBool *bp) { int i; if (!(cx->xmlSettingFlags & XSF_CACHE_VALID) && !FillSettingsCache(cx)) return JS_FALSE; for (i = 0; xml_static_props[i].name; i++) { if (!strcmp(xml_static_props[i].name, name)) { *bp = (cx->xmlSettingFlags & JS_BIT(i)) != 0; return JS_TRUE; } } *bp = JS_FALSE; return JS_TRUE; } static JSBool GetUint32XMLSetting(JSContext *cx, const char *name, uint32 *uip) { jsval v; return GetXMLSetting(cx, name, &v) && js_ValueToECMAUint32(cx, v, uip); } static JSBool GetXMLSettingFlags(JSContext *cx, uintN *flagsp) { JSBool flag; /* Just get the first flag to validate the setting flags cache. */ if (!GetBooleanXMLSetting(cx, js_ignoreComments_str, &flag)) return JS_FALSE; *flagsp = cx->xmlSettingFlags; return JS_TRUE; } static JSXML * ParseXMLSource(JSContext *cx, JSString *src) { jsval nsval; JSXMLNamespace *ns; size_t urilen, srclen, length, offset, dstlen; jschar *chars; const jschar *srcp, *endp; void *mark; JSTokenStream *ts; uintN lineno; JSStackFrame *fp; JSOp op; JSParseNode *pn; JSXML *xml; JSXMLArray nsarray; uintN flags; static const char prefix[] = ""; static const char suffix[] = ""; #define constrlen(constr) (sizeof(constr) - 1) if (!js_GetDefaultXMLNamespace(cx, &nsval)) return NULL; ns = (JSXMLNamespace *) JS_GetPrivate(cx, JSVAL_TO_OBJECT(nsval)); urilen = JSSTRING_LENGTH(ns->uri); srclen = JSSTRING_LENGTH(src); length = constrlen(prefix) + urilen + constrlen(middle) + srclen + constrlen(suffix); chars = (jschar *) JS_malloc(cx, (length + 1) * sizeof(jschar)); if (!chars) return NULL; dstlen = length; js_InflateStringToBuffer(cx, prefix, constrlen(prefix), chars, &dstlen); offset = dstlen; js_strncpy(chars + offset, JSSTRING_CHARS(ns->uri), urilen); offset += urilen; dstlen = length - offset + 1; js_InflateStringToBuffer(cx, middle, constrlen(middle), chars + offset, &dstlen); offset += dstlen; srcp = JSSTRING_CHARS(src); js_strncpy(chars + offset, srcp, srclen); offset += srclen; dstlen = length - offset + 1; js_InflateStringToBuffer(cx, suffix, constrlen(suffix), chars + offset, &dstlen); chars [offset + dstlen] = 0; mark = JS_ARENA_MARK(&cx->tempPool); ts = js_NewBufferTokenStream(cx, chars, length); if (!ts) return NULL; for (fp = cx->fp; fp && !fp->pc; fp = fp->down) continue; if (fp) { op = (JSOp) *fp->pc; if (op == JSOP_TOXML || op == JSOP_TOXMLLIST) { ts->filename = fp->script->filename; lineno = js_PCToLineNumber(cx, fp->script, fp->pc); for (endp = srcp + srclen; srcp < endp; srcp++) if (*srcp == '\n') --lineno; ts->lineno = lineno; } } JS_KEEP_ATOMS(cx->runtime); pn = js_ParseXMLTokenStream(cx, cx->fp->scopeChain, ts, JS_FALSE); xml = NULL; if (pn && XMLArrayInit(cx, &nsarray, 1)) { if (GetXMLSettingFlags(cx, &flags)) xml = ParseNodeToXML(cx, pn, &nsarray, flags); XMLArrayFinish(cx, &nsarray); } JS_UNKEEP_ATOMS(cx->runtime); JS_ARENA_RELEASE(&cx->tempPool, mark); JS_free(cx, chars); return xml; #undef constrlen } /* * Errata in 10.3.1, 10.4.1, and 13.4.4.24 (at least). * * 10.3.1 Step 6(a) fails to NOTE that implementations that do not enforce * the constraint: * * for all x belonging to XML: * x.[[InScopeNamespaces]] >= x.[[Parent]].[[InScopeNamespaces]] * * must union x.[[InScopeNamespaces]] into x[0].[[InScopeNamespaces]] here * (in new sub-step 6(a), renumbering the others to (b) and (c)). * * Same goes for 10.4.1 Step 7(a). * * In order for XML.prototype.namespaceDeclarations() to work correctly, the * default namespace thereby unioned into x[0].[[InScopeNamespaces]] must be * flagged as not declared, so that 13.4.4.24 Step 8(a) can exclude all such * undeclared namespaces associated with x not belonging to ancestorNS. */ static JSXML * OrphanXMLChild(JSContext *cx, JSXML *xml, uint32 i) { JSXMLNamespace *ns; ns = XMLARRAY_MEMBER(&xml->xml_namespaces, 0, JSXMLNamespace); xml = XMLARRAY_MEMBER(&xml->xml_kids, i, JSXML); if (!ns || !xml) return xml; if (xml->xml_class == JSXML_CLASS_ELEMENT) { if (!XMLARRAY_APPEND(cx, &xml->xml_namespaces, ns)) return NULL; ns->declared = JS_FALSE; } xml->parent = NULL; return xml; } static JSObject * ToXML(JSContext *cx, jsval v) { JSObject *obj; JSXML *xml; JSClass *clasp; JSString *str; uint32 length; if (JSVAL_IS_PRIMITIVE(v)) { if (JSVAL_IS_NULL(v) || JSVAL_IS_VOID(v)) goto bad; } else { obj = JSVAL_TO_OBJECT(v); if (OBJECT_IS_XML(cx, obj)) { xml = (JSXML *) JS_GetPrivate(cx, obj); if (xml->xml_class == JSXML_CLASS_LIST) { if (xml->xml_kids.length != 1) goto bad; xml = XMLARRAY_MEMBER(&xml->xml_kids, 0, JSXML); if (xml) { JS_ASSERT(xml->xml_class != JSXML_CLASS_LIST); return js_GetXMLObject(cx, xml); } } return obj; } clasp = OBJ_GET_CLASS(cx, obj); if (clasp->flags & JSCLASS_DOCUMENT_OBSERVER) { JS_ASSERT(0); } if (clasp != &js_StringClass && clasp != &js_NumberClass && clasp != &js_BooleanClass) { goto bad; } } str = js_ValueToString(cx, v); if (!str) return NULL; if (IS_EMPTY(str)) { length = 0; #ifdef __GNUC__ /* suppress bogus gcc warnings */ xml = NULL; #endif } else { xml = ParseXMLSource(cx, str); if (!xml) return NULL; length = JSXML_LENGTH(xml); } if (length == 0) { obj = js_NewXMLObject(cx, JSXML_CLASS_TEXT); if (!obj) return NULL; } else if (length == 1) { xml = OrphanXMLChild(cx, xml, 0); if (!xml) return NULL; obj = js_GetXMLObject(cx, xml); if (!obj) return NULL; } else { JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_SYNTAX_ERROR); return NULL; } return obj; bad: str = js_DecompileValueGenerator(cx, JSDVG_IGNORE_STACK, v, NULL); if (str) { JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_BAD_XML_CONVERSION, JS_GetStringBytes(str)); } return NULL; } static JSBool Append(JSContext *cx, JSXML *list, JSXML *kid); static JSObject * ToXMLList(JSContext *cx, jsval v) { JSObject *obj, *listobj; JSXML *xml, *list, *kid; JSClass *clasp; JSString *str; uint32 i, length; if (JSVAL_IS_PRIMITIVE(v)) { if (JSVAL_IS_NULL(v) || JSVAL_IS_VOID(v)) goto bad; } else { obj = JSVAL_TO_OBJECT(v); if (OBJECT_IS_XML(cx, obj)) { xml = (JSXML *) JS_GetPrivate(cx, obj); if (xml->xml_class != JSXML_CLASS_LIST) { listobj = js_NewXMLObject(cx, JSXML_CLASS_LIST); if (!listobj) return NULL; list = (JSXML *) JS_GetPrivate(cx, listobj); if (!Append(cx, list, xml)) return NULL; return listobj; } return obj; } clasp = OBJ_GET_CLASS(cx, obj); if (clasp->flags & JSCLASS_DOCUMENT_OBSERVER) { JS_ASSERT(0); } if (clasp != &js_StringClass && clasp != &js_NumberClass && clasp != &js_BooleanClass) { goto bad; } } str = js_ValueToString(cx, v); if (!str) return NULL; if (IS_EMPTY(str)) { xml = NULL; length = 0; } else { if (!js_EnterLocalRootScope(cx)) return NULL; xml = ParseXMLSource(cx, str); if (!xml) { js_LeaveLocalRootScope(cx); return NULL; } length = JSXML_LENGTH(xml); } listobj = js_NewXMLObject(cx, JSXML_CLASS_LIST); if (listobj) { list = (JSXML *) JS_GetPrivate(cx, listobj); for (i = 0; i < length; i++) { kid = OrphanXMLChild(cx, xml, i); if (!kid || !Append(cx, list, kid)) { listobj = NULL; break; } } } if (xml) js_LeaveLocalRootScopeWithResult(cx, (jsval) listobj); return listobj; bad: str = js_DecompileValueGenerator(cx, JSDVG_IGNORE_STACK, v, NULL); if (str) { JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_BAD_XMLLIST_CONVERSION, JS_GetStringBytes(str)); } return NULL; } /* * ECMA-357 10.2.1 Steps 5-7 pulled out as common subroutines of XMLToXMLString * and their library-public js_* counterparts. The guts of MakeXMLCDataString, * MakeXMLCommentString, and MakeXMLPIString are further factored into a common * MakeXMLSpecialString subroutine. * * These functions take ownership of sb->base, if sb is non-null, in all cases * of success or failure. */ static JSString * MakeXMLSpecialString(JSContext *cx, JSStringBuffer *sb, JSString *str, JSString *str2, const jschar *prefix, size_t prefixlength, const jschar *suffix, size_t suffixlength) { JSStringBuffer localSB; size_t length, length2, newlength; jschar *bp, *base; if (!sb) { sb = &localSB; js_InitStringBuffer(sb); } length = JSSTRING_LENGTH(str); length2 = str2 ? JSSTRING_LENGTH(str2) : 0; newlength = STRING_BUFFER_OFFSET(sb) + prefixlength + length + ((length2 != 0) ? 1 + length2 : 0) + suffixlength; bp = base = (jschar *) JS_realloc(cx, sb->base, (newlength + 1) * sizeof(jschar)); if (!bp) { js_FinishStringBuffer(sb); return NULL; } bp += STRING_BUFFER_OFFSET(sb); js_strncpy(bp, prefix, prefixlength); bp += prefixlength; js_strncpy(bp, JSSTRING_CHARS(str), length); bp += length; if (length2 != 0) { *bp++ = (jschar) ' '; js_strncpy(bp, JSSTRING_CHARS(str2), length2); bp += length2; } js_strncpy(bp, suffix, suffixlength); bp[suffixlength] = 0; str = js_NewString(cx, base, newlength, 0); if (!str) free(base); return str; } static JSString * MakeXMLCDATAString(JSContext *cx, JSStringBuffer *sb, JSString *str) { static const jschar cdata_prefix_ucNstr[] = {'<', '!', '[', 'C', 'D', 'A', 'T', 'A', '['}; static const jschar cdata_suffix_ucNstr[] = {']', ']', '>'}; return MakeXMLSpecialString(cx, sb, str, NULL, cdata_prefix_ucNstr, 9, cdata_suffix_ucNstr, 3); } static JSString * MakeXMLCommentString(JSContext *cx, JSStringBuffer *sb, JSString *str) { static const jschar comment_prefix_ucNstr[] = {'<', '!', '-', '-'}; static const jschar comment_suffix_ucNstr[] = {'-', '-', '>'}; return MakeXMLSpecialString(cx, sb, str, NULL, comment_prefix_ucNstr, 4, comment_suffix_ucNstr, 3); } static JSString * MakeXMLPIString(JSContext *cx, JSStringBuffer *sb, JSString *name, JSString *value) { static const jschar pi_prefix_ucNstr[] = {'<', '?'}; static const jschar pi_suffix_ucNstr[] = {'?', '>'}; return MakeXMLSpecialString(cx, sb, name, value, pi_prefix_ucNstr, 2, pi_suffix_ucNstr, 2); } /* * ECMA-357 10.2.1 17(d-g) pulled out into a common subroutine that appends * equals, a double quote, an attribute value, and a closing double quote. */ static void AppendAttributeValue(JSContext *cx, JSStringBuffer *sb, JSString *valstr) { js_AppendCString(sb, "=\""); valstr = js_EscapeAttributeValue(cx, valstr); if (!valstr) { free(sb->base); sb->base = STRING_BUFFER_ERROR_BASE; return; } js_AppendJSString(sb, valstr); js_AppendChar(sb, '"'); } /* * ECMA-357 10.2.1.1 EscapeElementValue helper method. * * This function takes ownership of sb->base, if sb is non-null, in all cases * of success or failure. */ static JSString * EscapeElementValue(JSContext *cx, JSStringBuffer *sb, JSString *str) { size_t length, newlength; const jschar *cp, *start, *end; jschar c; length = newlength = JSSTRING_LENGTH(str); for (cp = start = JSSTRING_CHARS(str), end = cp + length; cp < end; cp++) { c = *cp; if (c == '<' || c == '>') newlength += 3; else if (c == '&') newlength += 4; if (newlength < length) { JS_ReportOutOfMemory(cx); return NULL; } } if ((sb && STRING_BUFFER_OFFSET(sb) != 0) || newlength > length) { JSStringBuffer localSB; if (!sb) { sb = &localSB; js_InitStringBuffer(sb); } if (!sb->grow(sb, newlength)) { JS_ReportOutOfMemory(cx); return NULL; } for (cp = start; cp < end; cp++) { c = *cp; if (c == '<') js_AppendCString(sb, js_lt_entity_str); else if (c == '>') js_AppendCString(sb, js_gt_entity_str); else if (c == '&') js_AppendCString(sb, js_amp_entity_str); else js_AppendChar(sb, c); } JS_ASSERT(STRING_BUFFER_OK(sb)); str = js_NewString(cx, sb->base, STRING_BUFFER_OFFSET(sb), 0); if (!str) js_FinishStringBuffer(sb); } return str; } /* * ECMA-357 10.2.1.2 EscapeAttributeValue helper method. * This function takes ownership of sb->base, if sb is non-null, in all cases. */ static JSString * EscapeAttributeValue(JSContext *cx, JSStringBuffer *sb, JSString *str) { size_t length, newlength; const jschar *cp, *start, *end; jschar c; length = newlength = JSSTRING_LENGTH(str); for (cp = start = JSSTRING_CHARS(str), end = cp + length; cp < end; cp++) { c = *cp; if (c == '"') newlength += 5; else if (c == '<') newlength += 3; else if (c == '&' || c == '\n' || c == '\r' || c == '\t') newlength += 4; if (newlength < length) { JS_ReportOutOfMemory(cx); return NULL; } } if ((sb && STRING_BUFFER_OFFSET(sb) != 0) || newlength > length) { JSStringBuffer localSB; if (!sb) { sb = &localSB; js_InitStringBuffer(sb); } if (!sb->grow(sb, newlength)) { JS_ReportOutOfMemory(cx); return NULL; } for (cp = start; cp < end; cp++) { c = *cp; if (c == '"') js_AppendCString(sb, js_quot_entity_str); else if (c == '<') js_AppendCString(sb, js_lt_entity_str); else if (c == '&') js_AppendCString(sb, js_amp_entity_str); else if (c == '\n') js_AppendCString(sb, " "); else if (c == '\r') js_AppendCString(sb, " "); else if (c == '\t') js_AppendCString(sb, " "); else js_AppendChar(sb, c); } JS_ASSERT(STRING_BUFFER_OK(sb)); str = js_NewString(cx, sb->base, STRING_BUFFER_OFFSET(sb), 0); if (!str) js_FinishStringBuffer(sb); } return str; } /* 13.3.5.4 [[GetNamespace]]([InScopeNamespaces]) */ static JSXMLNamespace * GetNamespace(JSContext *cx, JSXMLQName *qn, const JSXMLArray *inScopeNSes) { JSXMLNamespace *match, *ns; uint32 i, n; jsval argv[2]; JSObject *nsobj; JS_ASSERT(qn->uri); if (!qn->uri) { JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_BAD_XML_NAMESPACE, qn->prefix ? js_ValueToPrintableString(cx, STRING_TO_JSVAL(qn->prefix)) : js_type_strs[JSTYPE_VOID]); return NULL; } /* Look for a matching namespace in inScopeNSes, if provided. */ match = NULL; if (inScopeNSes) { for (i = 0, n = inScopeNSes->length; i < n; i++) { ns = XMLARRAY_MEMBER(inScopeNSes, i, JSXMLNamespace); if (!ns) continue; /* * Erratum, very tricky, and not specified in ECMA-357 13.3.5.4: * If we preserve prefixes, we must match null qn->prefix against * an empty ns->prefix, in order to avoid generating redundant * prefixed and default namespaces for cases such as: * * x = * print(x.toXMLString()); * * Per 10.3.2.1, the namespace attribute in t has an empty string * prefix (*not* a null prefix), per 10.3.2.1 Step 6(h)(i)(1): * * 1. If the [local name] property of a is "xmlns" * a. Map ns.prefix to the empty string * * But t's name has a null prefix in this implementation, meaning * *undefined*, per 10.3.2.1 Step 6(c)'s NOTE (which refers to * the http://www.w3.org/TR/xml-infoset/ spec, item 2.2.3, without * saying how "no value" maps to an ECMA-357 value -- but it must * map to the *undefined* prefix value). * * Since "" != undefined (or null, in the current implementation) * the ECMA-357 spec will fail to match in [[GetNamespace]] called * on t with argument {} U {(prefix="", uri="http://foo.com")}. * This spec bug leads to ToXMLString results that duplicate the * declared namespace. */ if (js_EqualStrings(ns->uri, qn->uri) && (ns->prefix == qn->prefix || ((ns->prefix && qn->prefix) ? js_EqualStrings(ns->prefix, qn->prefix) : IS_EMPTY(ns->prefix ? ns->prefix : qn->prefix)))) { match = ns; break; } } } /* If we didn't match, make a new namespace from qn. */ if (!match) { argv[0] = qn->prefix ? STRING_TO_JSVAL(qn->prefix) : JSVAL_VOID; argv[1] = STRING_TO_JSVAL(qn->uri); nsobj = js_ConstructObject(cx, &js_NamespaceClass.base, NULL, NULL, 2, argv); if (!nsobj) return NULL; match = (JSXMLNamespace *) JS_GetPrivate(cx, nsobj); } return match; } static JSString * GeneratePrefix(JSContext *cx, JSString *uri, JSXMLArray *decls) { const jschar *cp, *start, *end; size_t length, newlength, offset; uint32 i, n, m, serial; jschar *bp, *dp; JSBool done; JSXMLNamespace *ns; JSString *prefix; JS_ASSERT(!IS_EMPTY(uri)); /* * If there are no *declared* namespaces, skip all collision detection and * return a short prefix quickly; an example of such a situation: * * var x = ; * var n = new Namespace("http://example.com/"); * x.@n::att = "val"; * x.toXMLString(); * * This is necessary for various log10 uses below to be valid. */ if (decls->length == 0) return JS_NewStringCopyZ(cx, "a"); /* * Try peeling off the last filename suffix or pathname component till * we have a valid XML name. This heuristic will prefer "xul" given * ".../there.is.only.xul", "xbl" given ".../xbl", and "xbl2" given any * likely URI of the form ".../xbl2/2005". */ start = JSSTRING_CHARS(uri); cp = end = start + JSSTRING_LENGTH(uri); while (--cp > start) { if (*cp == '.' || *cp == '/' || *cp == ':') { ++cp; length = PTRDIFF(end, cp, jschar); if (IsXMLName(cp, length) && !STARTS_WITH_XML(cp, length)) break; end = --cp; } } length = PTRDIFF(end, cp, jschar); /* * If the namespace consisted only of non-XML names or names that begin * case-insensitively with "xml", arbitrarily create a prefix consisting * of 'a's of size length (allowing dp-calculating code to work with or * without this branch executing) plus the space for storing a hyphen and * the serial number (avoiding reallocation if a collision happens). */ bp = (jschar *) cp; newlength = length; if (STARTS_WITH_XML(cp, length) || !IsXMLName(cp, length)) { newlength = length + 2 + (size_t) log10(decls->length); bp = (jschar *) JS_malloc(cx, (newlength + 1) * sizeof(jschar)); if (!bp) return NULL; bp[newlength] = 0; for (i = 0; i < newlength; i++) bp[i] = 'a'; } /* * Now search through decls looking for a collision. If we collide with * an existing prefix, start tacking on a hyphen and a serial number. */ serial = 0; do { done = JS_TRUE; for (i = 0, n = decls->length; i < n; i++) { ns = XMLARRAY_MEMBER(decls, i, JSXMLNamespace); if (ns && ns->prefix && JSSTRING_LENGTH(ns->prefix) == newlength && !memcmp(JSSTRING_CHARS(ns->prefix), bp, newlength * sizeof(jschar))) { if (bp == cp) { newlength = length + 2 + (size_t) log10(n); bp = (jschar *) JS_malloc(cx, (newlength + 1) * sizeof(jschar)); if (!bp) return NULL; js_strncpy(bp, cp, length); } ++serial; JS_ASSERT(serial <= n); dp = bp + length + 2 + (size_t) log10(serial); *dp = 0; for (m = serial; m != 0; m /= 10) *--dp = (jschar)('0' + m % 10); *--dp = '-'; JS_ASSERT(dp == bp + length); done = JS_FALSE; break; } } } while (!done); if (bp == cp) { offset = PTRDIFF(cp, start, jschar); prefix = js_NewDependentString(cx, uri, offset, length, 0); } else { prefix = js_NewString(cx, bp, newlength, 0); if (!prefix) JS_free(cx, bp); } return prefix; } static JSBool namespace_match(const void *a, const void *b) { const JSXMLNamespace *nsa = (const JSXMLNamespace *) a; const JSXMLNamespace *nsb = (const JSXMLNamespace *) b; if (nsb->prefix) return nsa->prefix && js_EqualStrings(nsa->prefix, nsb->prefix); return js_EqualStrings(nsa->uri, nsb->uri); } /* ECMA-357 10.2.1 and 10.2.2 */ static JSString * XMLToXMLString(JSContext *cx, JSXML *xml, const JSXMLArray *ancestorNSes, uintN indentLevel) { JSBool pretty, indentKids; JSStringBuffer sb; JSString *str, *prefix, *kidstr; JSXMLArrayCursor cursor; uint32 i, n; JSXMLArray empty, decls, ancdecls; JSXMLNamespace *ns, *ns2; uintN nextIndentLevel; JSXML *attr, *kid; if (!GetBooleanXMLSetting(cx, js_prettyPrinting_str, &pretty)) return NULL; js_InitStringBuffer(&sb); if (pretty) js_RepeatChar(&sb, ' ', indentLevel); str = NULL; switch (xml->xml_class) { case JSXML_CLASS_TEXT: /* Step 4. */ if (pretty) { str = ChompXMLWhitespace(cx, xml->xml_value); if (!str) return NULL; } else { str = xml->xml_value; } return EscapeElementValue(cx, &sb, str); case JSXML_CLASS_ATTRIBUTE: /* Step 5. */ return EscapeAttributeValue(cx, &sb, xml->xml_value); case JSXML_CLASS_COMMENT: /* Step 6. */ return MakeXMLCommentString(cx, &sb, xml->xml_value); case JSXML_CLASS_PROCESSING_INSTRUCTION: /* Step 7. */ return MakeXMLPIString(cx, &sb, xml->name->localName, xml->xml_value); case JSXML_CLASS_LIST: /* ECMA-357 10.2.2. */ XMLArrayCursorInit(&cursor, &xml->xml_kids); i = 0; while ((kid = (JSXML *) XMLArrayCursorNext(&cursor)) != NULL) { if (pretty && i != 0) js_AppendChar(&sb, '\n'); kidstr = XMLToXMLString(cx, kid, ancestorNSes, indentLevel); if (!kidstr) break; js_AppendJSString(&sb, kidstr); ++i; } XMLArrayCursorFinish(&cursor); if (kid) goto list_out; if (!sb.base) { if (!STRING_BUFFER_OK(&sb)) { JS_ReportOutOfMemory(cx); return NULL; } return cx->runtime->emptyString; } str = js_NewString(cx, sb.base, STRING_BUFFER_OFFSET(&sb), 0); list_out: if (!str) js_FinishStringBuffer(&sb); return str; default:; } /* After this point, control must flow through label out: to exit. */ if (!js_EnterLocalRootScope(cx)) return NULL; /* ECMA-357 10.2.1 step 8 onward: handle ToXMLString on an XML element. */ if (!ancestorNSes) { XMLArrayInit(cx, &empty, 0); ancestorNSes = ∅ } XMLArrayInit(cx, &decls, 0); ancdecls.capacity = 0; /* Clone in-scope namespaces not in ancestorNSes into decls. */ XMLArrayCursorInit(&cursor, &xml->xml_namespaces); while ((ns = (JSXMLNamespace *) XMLArrayCursorNext(&cursor)) != NULL) { if (!ns->declared) continue; if (!XMLARRAY_HAS_MEMBER(ancestorNSes, ns, namespace_identity)) { /* NOTE: may want to exclude unused namespaces here. */ ns2 = js_NewXMLNamespace(cx, ns->prefix, ns->uri, JS_TRUE); if (!ns2 || !XMLARRAY_APPEND(cx, &decls, ns2)) break; } } XMLArrayCursorFinish(&cursor); if (ns) goto out; /* * Union ancestorNSes and decls into ancdecls. Note that ancdecls does * not own its member references. In the spec, ancdecls has no name, but * is always written out as (AncestorNamespaces U namespaceDeclarations). */ if (!XMLArrayInit(cx, &ancdecls, ancestorNSes->length + decls.length)) goto out; for (i = 0, n = ancestorNSes->length; i < n; i++) { ns2 = XMLARRAY_MEMBER(ancestorNSes, i, JSXMLNamespace); if (!ns2) continue; JS_ASSERT(!XMLARRAY_HAS_MEMBER(&decls, ns2, namespace_identity)); if (!XMLARRAY_APPEND(cx, &ancdecls, ns2)) goto out; } for (i = 0, n = decls.length; i < n; i++) { ns2 = XMLARRAY_MEMBER(&decls, i, JSXMLNamespace); if (!ns2) continue; JS_ASSERT(!XMLARRAY_HAS_MEMBER(&ancdecls, ns2, namespace_identity)); if (!XMLARRAY_APPEND(cx, &ancdecls, ns2)) goto out; } /* Step 11, except we don't clone ns unless its prefix is undefined. */ ns = GetNamespace(cx, xml->name, &ancdecls); if (!ns) goto out; /* Step 12 (NULL means *undefined* here), plus the deferred ns cloning. */ if (!ns->prefix) { /* * Create a namespace prefix that isn't used by any member of decls. * Assign the new prefix to a copy of ns. Flag this namespace as if * it were declared, for assertion-testing's sake later below. * * Erratum: if ns->prefix and xml->name are both null (*undefined* in * ECMA-357), we know that xml was named using the default namespace * (proof: see GetNamespace and the Namespace constructor called with * two arguments). So we ought not generate a new prefix here, when * we can declare ns as the default namespace for xml. * * This helps descendants inherit the namespace instead of redundantly * redeclaring it with generated prefixes in each descendant. */ if (!xml->name->prefix) { prefix = cx->runtime->emptyString; } else { prefix = GeneratePrefix(cx, ns->uri, &ancdecls); if (!prefix) goto out; } ns = js_NewXMLNamespace(cx, prefix, ns->uri, JS_TRUE); if (!ns) goto out; /* * If the xml->name was unprefixed, we must remove any declared default * namespace from decls before appending ns. How can you get a default * namespace in decls that doesn't match the one from name? Apparently * by calling x.setNamespace(ns) where ns has no prefix. The other way * to fix this is to update x's in-scope namespaces when setNamespace * is called, but that's not specified by ECMA-357. * * Likely Erratum here, depending on whether the lack of update to x's * in-scope namespace in XML.prototype.setNamespace (13.4.4.36) is an * erratum or not. Note that changing setNamespace to update the list * of in-scope namespaces will change x.namespaceDeclarations(). */ if (IS_EMPTY(prefix)) { i = XMLArrayFindMember(&decls, ns, namespace_match); if (i != XML_NOT_FOUND) XMLArrayDelete(cx, &decls, i, JS_TRUE); } /* * In the spec, ancdecls has no name, but is always written out as * (AncestorNamespaces U namespaceDeclarations). Since we compute * that union in ancdecls, any time we append a namespace strong * ref to decls, we must also append a weak ref to ancdecls. Order * matters here: code at label out: releases strong refs in decls. */ if (!XMLARRAY_APPEND(cx, &ancdecls, ns) || !XMLARRAY_APPEND(cx, &decls, ns)) { goto out; } } /* Format the element or point-tag into sb. */ js_AppendChar(&sb, '<'); if (ns->prefix && !IS_EMPTY(ns->prefix)) { js_AppendJSString(&sb, ns->prefix); js_AppendChar(&sb, ':'); } js_AppendJSString(&sb, xml->name->localName); /* * Step 16 makes a union to avoid writing two loops in step 17, to share * common attribute value appending spec-code. We prefer two loops for * faster code and less data overhead. */ /* Step 17(b): append attributes. */ XMLArrayCursorInit(&cursor, &xml->xml_attrs); while ((attr = (JSXML *) XMLArrayCursorNext(&cursor)) != NULL) { js_AppendChar(&sb, ' '); ns2 = GetNamespace(cx, attr->name, &ancdecls); if (!ns2) break; /* 17(b)(ii): NULL means *undefined* here. */ if (!ns2->prefix) { prefix = GeneratePrefix(cx, ns2->uri, &ancdecls); if (!prefix) break; /* Again, we avoid copying ns2 until we know it's prefix-less. */ ns2 = js_NewXMLNamespace(cx, prefix, ns2->uri, JS_TRUE); if (!ns2) break; /* * In the spec, ancdecls has no name, but is always written out as * (AncestorNamespaces U namespaceDeclarations). Since we compute * that union in ancdecls, any time we append a namespace strong * ref to decls, we must also append a weak ref to ancdecls. Order * matters here: code at label out: releases strong refs in decls. */ if (!XMLARRAY_APPEND(cx, &ancdecls, ns2) || !XMLARRAY_APPEND(cx, &decls, ns2)) { break; } } /* 17(b)(iii). */ if (!IS_EMPTY(ns2->prefix)) { js_AppendJSString(&sb, ns2->prefix); js_AppendChar(&sb, ':'); } /* 17(b)(iv). */ js_AppendJSString(&sb, attr->name->localName); /* 17(d-g). */ AppendAttributeValue(cx, &sb, attr->xml_value); } XMLArrayCursorFinish(&cursor); if (attr) goto out; /* Step 17(c): append XML namespace declarations. */ XMLArrayCursorInit(&cursor, &decls); while ((ns2 = (JSXMLNamespace *) XMLArrayCursorNext(&cursor)) != NULL) { JS_ASSERT(ns2->declared); js_AppendCString(&sb, " xmlns"); /* 17(c)(ii): NULL means *undefined* here. */ if (!ns2->prefix) { prefix = GeneratePrefix(cx, ns2->uri, &ancdecls); if (!prefix) break; ns2->prefix = prefix; } /* 17(c)(iii). */ if (!IS_EMPTY(ns2->prefix)) { js_AppendChar(&sb, ':'); js_AppendJSString(&sb, ns2->prefix); } /* 17(d-g). */ AppendAttributeValue(cx, &sb, ns2->uri); } XMLArrayCursorFinish(&cursor); if (ns2) goto out; /* Step 18: handle point tags. */ n = xml->xml_kids.length; if (n == 0) { js_AppendCString(&sb, "/>"); } else { /* Steps 19 through 25: handle element content, and open the end-tag. */ js_AppendChar(&sb, '>'); indentKids = n > 1 || (n == 1 && (kid = XMLARRAY_MEMBER(&xml->xml_kids, 0, JSXML)) && kid->xml_class != JSXML_CLASS_TEXT); if (pretty && indentKids) { if (!GetUint32XMLSetting(cx, js_prettyIndent_str, &i)) goto out; nextIndentLevel = indentLevel + i; } else { nextIndentLevel = 0; } XMLArrayCursorInit(&cursor, &xml->xml_kids); while ((kid = (JSXML *) XMLArrayCursorNext(&cursor)) != NULL) { if (pretty && indentKids) js_AppendChar(&sb, '\n'); kidstr = XMLToXMLString(cx, kid, &ancdecls, nextIndentLevel); if (!kidstr) break; js_AppendJSString(&sb, kidstr); } XMLArrayCursorFinish(&cursor); if (kid) goto out; if (pretty && indentKids) { js_AppendChar(&sb, '\n'); js_RepeatChar(&sb, ' ', indentLevel); } js_AppendCString(&sb, "prefix && !IS_EMPTY(ns->prefix)) { js_AppendJSString(&sb, ns->prefix); js_AppendChar(&sb, ':'); } /* Step 27. */ js_AppendJSString(&sb, xml->name->localName); js_AppendChar(&sb, '>'); } if (!STRING_BUFFER_OK(&sb)) { JS_ReportOutOfMemory(cx); goto out; } str = js_NewString(cx, sb.base, STRING_BUFFER_OFFSET(&sb), 0); out: js_LeaveLocalRootScopeWithResult(cx, STRING_TO_JSVAL(str)); if (!str && STRING_BUFFER_OK(&sb)) js_FinishStringBuffer(&sb); XMLArrayFinish(cx, &decls); if (ancdecls.capacity != 0) XMLArrayFinish(cx, &ancdecls); return str; } /* ECMA-357 10.2 */ static JSString * ToXMLString(JSContext *cx, jsval v) { JSObject *obj; JSString *str; JSXML *xml; if (JSVAL_IS_NULL(v) || JSVAL_IS_VOID(v)) { JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_BAD_XML_CONVERSION, js_type_strs[JSVAL_IS_NULL(v) ? JSTYPE_NULL : JSTYPE_VOID]); return NULL; } if (JSVAL_IS_BOOLEAN(v) || JSVAL_IS_NUMBER(v)) return js_ValueToString(cx, v); if (JSVAL_IS_STRING(v)) return EscapeElementValue(cx, NULL, JSVAL_TO_STRING(v)); obj = JSVAL_TO_OBJECT(v); if (!OBJECT_IS_XML(cx, obj)) { if (!OBJ_DEFAULT_VALUE(cx, obj, JSTYPE_STRING, &v)) return NULL; str = js_ValueToString(cx, v); if (!str) return NULL; return EscapeElementValue(cx, NULL, str); } /* Handle non-element cases in this switch, returning from each case. */ xml = (JSXML *) JS_GetPrivate(cx, obj); return XMLToXMLString(cx, xml, NULL, 0); } static JSXMLQName * ToAttributeName(JSContext *cx, jsval v) { JSString *name, *uri, *prefix; JSObject *obj; JSClass *clasp; JSXMLQName *qn; JSTempValueRooter tvr; if (JSVAL_IS_STRING(v)) { name = JSVAL_TO_STRING(v); uri = prefix = cx->runtime->emptyString; } else { if (JSVAL_IS_PRIMITIVE(v)) { name = js_DecompileValueGenerator(cx, JSDVG_IGNORE_STACK, v, NULL); if (name) { JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_BAD_XML_ATTR_NAME, JS_GetStringBytes(name)); } return NULL; } obj = JSVAL_TO_OBJECT(v); clasp = OBJ_GET_CLASS(cx, obj); if (clasp == &js_AttributeNameClass) return (JSXMLQName *) JS_GetPrivate(cx, obj); if (clasp == &js_QNameClass.base) { qn = (JSXMLQName *) JS_GetPrivate(cx, obj); uri = qn->uri; prefix = qn->prefix; name = qn->localName; } else { if (clasp == &js_AnyNameClass) { name = ATOM_TO_STRING(cx->runtime->atomState.starAtom); } else { name = js_ValueToString(cx, v); if (!name) return NULL; } uri = prefix = cx->runtime->emptyString; } } qn = js_NewXMLQName(cx, uri, prefix, name); if (!qn) return NULL; JS_PUSH_TEMP_ROOT_GCTHING(cx, qn, &tvr); obj = js_GetAttributeNameObject(cx, qn); JS_POP_TEMP_ROOT(cx, &tvr); if (!obj) return NULL; return qn; } static JSXMLQName * ToXMLName(JSContext *cx, jsval v, jsid *funidp) { JSString *name; JSObject *obj; JSClass *clasp; uint32 index; JSXMLQName *qn; JSAtom *atom; if (JSVAL_IS_STRING(v)) { name = JSVAL_TO_STRING(v); } else { if (JSVAL_IS_PRIMITIVE(v)) { name = js_DecompileValueGenerator(cx, JSDVG_IGNORE_STACK, v, NULL); if (name) goto bad; return NULL; } obj = JSVAL_TO_OBJECT(v); clasp = OBJ_GET_CLASS(cx, obj); if (clasp == &js_AttributeNameClass || clasp == &js_QNameClass.base) goto out; if (clasp == &js_AnyNameClass) { name = ATOM_TO_STRING(cx->runtime->atomState.starAtom); goto construct; } name = js_ValueToString(cx, v); if (!name) return NULL; } /* * ECMA-357 10.6.1 step 1 seems to be incorrect. The spec says: * * 1. If ToString(ToNumber(P)) == ToString(P), throw a TypeError exception * * First, _P_ should be _s_, to refer to the given string. * * Second, why does ToXMLName applied to the string type throw TypeError * only for numeric literals without any leading or trailing whitespace? * * If the idea is to reject uint32 property names, then the check needs to * be stricter, to exclude hexadecimal and floating point literals. */ if (js_IdIsIndex(STRING_TO_JSVAL(name), &index)) goto bad; if (*JSSTRING_CHARS(name) == '@') { name = js_NewDependentString(cx, name, 1, JSSTRING_LENGTH(name) - 1, 0); if (!name) return NULL; *funidp = 0; return ToAttributeName(cx, STRING_TO_JSVAL(name)); } construct: v = STRING_TO_JSVAL(name); obj = js_ConstructObject(cx, &js_QNameClass.base, NULL, NULL, 1, &v); if (!obj) return NULL; out: qn = (JSXMLQName *) JS_GetPrivate(cx, obj); atom = cx->runtime->atomState.lazy.functionNamespaceURIAtom; if (qn->uri && atom && (qn->uri == ATOM_TO_STRING(atom) || js_EqualStrings(qn->uri, ATOM_TO_STRING(atom)))) { if (!JS_ValueToId(cx, STRING_TO_JSVAL(qn->localName), funidp)) return NULL; } else { *funidp = 0; } return qn; bad: JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_BAD_XML_NAME, js_ValueToPrintableString(cx, STRING_TO_JSVAL(name))); return NULL; } /* ECMA-357 9.1.1.13 XML [[AddInScopeNamespace]]. */ static JSBool AddInScopeNamespace(JSContext *cx, JSXML *xml, JSXMLNamespace *ns) { JSXMLNamespace *match, *ns2; uint32 i, n, m; if (xml->xml_class != JSXML_CLASS_ELEMENT) return JS_TRUE; /* NULL means *undefined* here -- see ECMA-357 9.1.1.13 step 2. */ if (!ns->prefix) { match = NULL; for (i = 0, n = xml->xml_namespaces.length; i < n; i++) { ns2 = XMLARRAY_MEMBER(&xml->xml_namespaces, i, JSXMLNamespace); if (ns2 && js_EqualStrings(ns2->uri, ns->uri)) { match = ns2; break; } } if (!match && !XMLARRAY_ADD_MEMBER(cx, &xml->xml_namespaces, n, ns)) return JS_FALSE; } else { if (IS_EMPTY(ns->prefix) && IS_EMPTY(xml->name->uri)) return JS_TRUE; match = NULL; #ifdef __GNUC__ /* suppress bogus gcc warnings */ m = XML_NOT_FOUND; #endif for (i = 0, n = xml->xml_namespaces.length; i < n; i++) { ns2 = XMLARRAY_MEMBER(&xml->xml_namespaces, i, JSXMLNamespace); if (ns2 && ns2->prefix && js_EqualStrings(ns2->prefix, ns->prefix)) { match = ns2; m = i; break; } } if (match && !js_EqualStrings(match->uri, ns->uri)) { ns2 = XMLARRAY_DELETE(cx, &xml->xml_namespaces, m, JS_TRUE, JSXMLNamespace); JS_ASSERT(ns2 == match); match->prefix = NULL; if (!AddInScopeNamespace(cx, xml, match)) return JS_FALSE; } if (!XMLARRAY_APPEND(cx, &xml->xml_namespaces, ns)) return JS_FALSE; } /* OPTION: enforce that descendants have superset namespaces. */ return JS_TRUE; } /* ECMA-357 9.2.1.6 XMLList [[Append]]. */ static JSBool Append(JSContext *cx, JSXML *list, JSXML *xml) { uint32 i, j, k, n; JSXML *kid; JS_ASSERT(list->xml_class == JSXML_CLASS_LIST); i = list->xml_kids.length; n = 1; if (xml->xml_class == JSXML_CLASS_LIST) { list->xml_target = xml->xml_target; list->xml_targetprop = xml->xml_targetprop; n = JSXML_LENGTH(xml); k = i + n; if (!XMLArraySetCapacity(cx, &list->xml_kids, k)) return JS_FALSE; for (j = 0; j < n; j++) { kid = XMLARRAY_MEMBER(&xml->xml_kids, j, JSXML); if (kid) XMLARRAY_SET_MEMBER(&list->xml_kids, i + j, kid); } return JS_TRUE; } list->xml_target = xml->parent; if (xml->xml_class == JSXML_CLASS_PROCESSING_INSTRUCTION) list->xml_targetprop = NULL; else list->xml_targetprop = xml->name; if (!XMLARRAY_ADD_MEMBER(cx, &list->xml_kids, i, xml)) return JS_FALSE; return JS_TRUE; } /* ECMA-357 9.1.1.7 XML [[DeepCopy]] and 9.2.1.7 XMLList [[DeepCopy]]. */ static JSXML * DeepCopyInLRS(JSContext *cx, JSXML *xml, uintN flags); static JSXML * DeepCopy(JSContext *cx, JSXML *xml, JSObject *obj, uintN flags) { JSXML *copy; JSBool ok; /* Our caller may not be protecting newborns with a local root scope. */ if (!js_EnterLocalRootScope(cx)) return NULL; copy = DeepCopyInLRS(cx, xml, flags); if (copy) { if (obj) { /* Caller provided the object for this copy, hook 'em up. */ ok = JS_SetPrivate(cx, obj, copy); if (ok) copy->object = obj; } else { ok = js_GetXMLObject(cx, copy) != NULL; } if (!ok) copy = NULL; } js_LeaveLocalRootScopeWithResult(cx, (jsval) copy); return copy; } /* * (i) We must be in a local root scope (InLRS). * (ii) parent must have a rooted object. * (iii) from's owning object must be locked if not thread-local. */ static JSBool DeepCopySetInLRS(JSContext *cx, JSXMLArray *from, JSXMLArray *to, JSXML *parent, uintN flags) { uint32 j, n; JSXMLArrayCursor cursor; JSBool ok; JSXML *kid, *kid2; JSString *str; JS_ASSERT(cx->localRootStack); n = from->length; if (!XMLArraySetCapacity(cx, to, n)) return JS_FALSE; XMLArrayCursorInit(&cursor, from); j = 0; ok = JS_TRUE; while ((kid = (JSXML *) XMLArrayCursorNext(&cursor)) != NULL) { if ((flags & XSF_IGNORE_COMMENTS) && kid->xml_class == JSXML_CLASS_COMMENT) { continue; } if ((flags & XSF_IGNORE_PROCESSING_INSTRUCTIONS) && kid->xml_class == JSXML_CLASS_PROCESSING_INSTRUCTION) { continue; } if ((flags & XSF_IGNORE_WHITESPACE) && (kid->xml_flags & XMLF_WHITESPACE_TEXT)) { continue; } kid2 = DeepCopyInLRS(cx, kid, flags); if (!kid2) { to->length = j; ok = JS_FALSE; break; } if ((flags & XSF_IGNORE_WHITESPACE) && n > 1 && kid2->xml_class == JSXML_CLASS_TEXT) { str = ChompXMLWhitespace(cx, kid2->xml_value); if (!str) { to->length = j; ok = JS_FALSE; break; } kid2->xml_value = str; } XMLARRAY_SET_MEMBER(to, j, kid2); ++j; if (parent->xml_class != JSXML_CLASS_LIST) kid2->parent = parent; } XMLArrayCursorFinish(&cursor); if (!ok) return JS_FALSE; if (j < n) XMLArrayTrim(to); return JS_TRUE; } static JSXML * DeepCopyInLRS(JSContext *cx, JSXML *xml, uintN flags) { JSXML *copy; JSXMLQName *qn; JSBool ok; uint32 i, n; JSXMLNamespace *ns, *ns2; /* Our caller must be protecting newborn objects. */ JS_ASSERT(cx->localRootStack); copy = js_NewXML(cx, xml->xml_class); if (!copy) return NULL; qn = xml->name; if (qn) { qn = js_NewXMLQName(cx, qn->uri, qn->prefix, qn->localName); if (!qn) { ok = JS_FALSE; goto out; } } copy->name = qn; copy->xml_flags = xml->xml_flags; if (JSXML_HAS_VALUE(xml)) { copy->xml_value = xml->xml_value; ok = JS_TRUE; } else { ok = DeepCopySetInLRS(cx, &xml->xml_kids, ©->xml_kids, copy, flags); if (!ok) goto out; if (xml->xml_class == JSXML_CLASS_LIST) { copy->xml_target = xml->xml_target; copy->xml_targetprop = xml->xml_targetprop; } else { n = xml->xml_namespaces.length; ok = XMLArraySetCapacity(cx, ©->xml_namespaces, n); if (!ok) goto out; for (i = 0; i < n; i++) { ns = XMLARRAY_MEMBER(&xml->xml_namespaces, i, JSXMLNamespace); if (!ns) continue; ns2 = js_NewXMLNamespace(cx, ns->prefix, ns->uri, ns->declared); if (!ns2) { copy->xml_namespaces.length = i; ok = JS_FALSE; goto out; } XMLARRAY_SET_MEMBER(©->xml_namespaces, i, ns2); } ok = DeepCopySetInLRS(cx, &xml->xml_attrs, ©->xml_attrs, copy, 0); if (!ok) goto out; } } out: if (!ok) return NULL; return copy; } static void ReportBadXMLName(JSContext *cx, jsval id) { JSString *name; name = js_DecompileValueGenerator(cx, JSDVG_IGNORE_STACK, id, NULL); if (name) { JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_BAD_XML_NAME, JS_GetStringBytes(name)); } } /* ECMA-357 9.1.1.4 XML [[DeleteByIndex]]. */ static JSBool DeleteByIndex(JSContext *cx, JSXML *xml, jsval id, jsval *vp) { uint32 index; JSXML *kid; if (!js_IdIsIndex(id, &index)) { ReportBadXMLName(cx, id); return JS_FALSE; } if (JSXML_HAS_KIDS(xml) && index < xml->xml_kids.length) { kid = XMLARRAY_MEMBER(&xml->xml_kids, index, JSXML); if (kid) kid->parent = NULL; XMLArrayDelete(cx, &xml->xml_kids, index, JS_TRUE); } *vp = JSVAL_TRUE; return JS_TRUE; } typedef JSBool (*JSXMLNameMatcher)(JSXMLQName *nameqn, JSXML *xml); static JSBool MatchAttrName(JSXMLQName *nameqn, JSXML *attr) { JSXMLQName *attrqn = attr->name; return (IS_STAR(nameqn->localName) || js_EqualStrings(attrqn->localName, nameqn->localName)) && (!nameqn->uri || js_EqualStrings(attrqn->uri, nameqn->uri)); } static JSBool MatchElemName(JSXMLQName *nameqn, JSXML *elem) { return (IS_STAR(nameqn->localName) || (elem->xml_class == JSXML_CLASS_ELEMENT && js_EqualStrings(elem->name->localName, nameqn->localName))) && (!nameqn->uri || (elem->xml_class == JSXML_CLASS_ELEMENT && js_EqualStrings(elem->name->uri, nameqn->uri))); } /* ECMA-357 9.1.1.8 XML [[Descendants]] and 9.2.1.8 XMLList [[Descendants]]. */ static JSBool DescendantsHelper(JSContext *cx, JSXML *xml, JSXMLQName *nameqn, JSXML *list) { uint32 i, n; JSXML *attr, *kid; if (xml->xml_class == JSXML_CLASS_ELEMENT && OBJ_GET_CLASS(cx, nameqn->object) == &js_AttributeNameClass) { for (i = 0, n = xml->xml_attrs.length; i < n; i++) { attr = XMLARRAY_MEMBER(&xml->xml_attrs, i, JSXML); if (attr && MatchAttrName(nameqn, attr)) { if (!Append(cx, list, attr)) return JS_FALSE; } } } for (i = 0, n = JSXML_LENGTH(xml); i < n; i++) { kid = XMLARRAY_MEMBER(&xml->xml_kids, i, JSXML); if (!kid) continue; if (OBJ_GET_CLASS(cx, nameqn->object) != &js_AttributeNameClass && MatchElemName(nameqn, kid)) { if (!Append(cx, list, kid)) return JS_FALSE; } if (!DescendantsHelper(cx, kid, nameqn, list)) return JS_FALSE; } return JS_TRUE; } static JSXML * Descendants(JSContext *cx, JSXML *xml, jsval id) { jsid funid; JSXMLQName *nameqn; JSObject *listobj; JSXML *list, *kid; uint32 i, n; JSBool ok; nameqn = ToXMLName(cx, id, &funid); if (!nameqn) return NULL; listobj = js_NewXMLObject(cx, JSXML_CLASS_LIST); if (!listobj) return NULL; list = (JSXML *) JS_GetPrivate(cx, listobj); if (funid) return list; /* * Protect nameqn's object and strings from GC by linking list to it * temporarily. The cx->newborn[GCX_OBJECT] GC root protects listobj, * which protects list. Any other object allocations occuring beneath * DescendantsHelper use local roots. */ list->name = nameqn; if (!js_EnterLocalRootScope(cx)) return NULL; if (xml->xml_class == JSXML_CLASS_LIST) { ok = JS_TRUE; for (i = 0, n = xml->xml_kids.length; i < n; i++) { kid = XMLARRAY_MEMBER(&xml->xml_kids, i, JSXML); if (kid && kid->xml_class == JSXML_CLASS_ELEMENT) { ok = DescendantsHelper(cx, kid, nameqn, list); if (!ok) break; } } } else { ok = DescendantsHelper(cx, xml, nameqn, list); } js_LeaveLocalRootScopeWithResult(cx, (jsval) list); if (!ok) return NULL; list->name = NULL; return list; } static JSBool xml_equality(JSContext *cx, JSObject *obj, jsval v, JSBool *bp); /* Recursive (JSXML *) parameterized version of Equals. */ static JSBool XMLEquals(JSContext *cx, JSXML *xml, JSXML *vxml, JSBool *bp) { JSXMLQName *qn, *vqn; uint32 i, j, n; JSXMLArrayCursor cursor, vcursor; JSXML *kid, *vkid, *attr, *vattr; JSBool ok; JSObject *xobj, *vobj; retry: if (xml->xml_class != vxml->xml_class) { if (xml->xml_class == JSXML_CLASS_LIST && xml->xml_kids.length == 1) { xml = XMLARRAY_MEMBER(&xml->xml_kids, 0, JSXML); if (xml) goto retry; } if (vxml->xml_class == JSXML_CLASS_LIST && vxml->xml_kids.length == 1) { vxml = XMLARRAY_MEMBER(&vxml->xml_kids, 0, JSXML); if (vxml) goto retry; } *bp = JS_FALSE; return JS_TRUE; } qn = xml->name; vqn = vxml->name; if (qn) { *bp = vqn && js_EqualStrings(qn->localName, vqn->localName) && js_EqualStrings(qn->uri, vqn->uri); } else { *bp = vqn == NULL; } if (!*bp) return JS_TRUE; if (JSXML_HAS_VALUE(xml)) { *bp = js_EqualStrings(xml->xml_value, vxml->xml_value); } else if (xml->xml_kids.length != vxml->xml_kids.length) { *bp = JS_FALSE; } else { XMLArrayCursorInit(&cursor, &xml->xml_kids); XMLArrayCursorInit(&vcursor, &vxml->xml_kids); for (;;) { kid = (JSXML *) XMLArrayCursorNext(&cursor); vkid = (JSXML *) XMLArrayCursorNext(&vcursor); if (!kid || !vkid) { *bp = !kid && !vkid; ok = JS_TRUE; break; } xobj = js_GetXMLObject(cx, kid); vobj = js_GetXMLObject(cx, vkid); ok = xobj && vobj && xml_equality(cx, xobj, OBJECT_TO_JSVAL(vobj), bp); if (!ok || !*bp) break; } XMLArrayCursorFinish(&vcursor); XMLArrayCursorFinish(&cursor); if (!ok) return JS_FALSE; if (*bp && xml->xml_class == JSXML_CLASS_ELEMENT) { n = xml->xml_attrs.length; if (n != vxml->xml_attrs.length) *bp = JS_FALSE; for (i = 0; *bp && i < n; i++) { attr = XMLARRAY_MEMBER(&xml->xml_attrs, i, JSXML); if (!attr) continue; j = XMLARRAY_FIND_MEMBER(&vxml->xml_attrs, attr, attr_identity); if (j == XML_NOT_FOUND) { *bp = JS_FALSE; break; } vattr = XMLARRAY_MEMBER(&vxml->xml_attrs, j, JSXML); if (!vattr) continue; *bp = js_EqualStrings(attr->xml_value, vattr->xml_value); } } } return JS_TRUE; } /* ECMA-357 9.1.1.9 XML [[Equals]] and 9.2.1.9 XMLList [[Equals]]. */ static JSBool Equals(JSContext *cx, JSXML *xml, jsval v, JSBool *bp) { JSObject *vobj; JSXML *vxml; if (JSVAL_IS_PRIMITIVE(v)) { *bp = JS_FALSE; if (xml->xml_class == JSXML_CLASS_LIST) { if (xml->xml_kids.length == 1) { vxml = XMLARRAY_MEMBER(&xml->xml_kids, 0, JSXML); if (!vxml) return JS_TRUE; vobj = js_GetXMLObject(cx, vxml); if (!vobj) return JS_FALSE; return js_XMLObjectOps.equality(cx, vobj, v, bp); } if (JSVAL_IS_VOID(v) && xml->xml_kids.length == 0) *bp = JS_TRUE; } } else { vobj = JSVAL_TO_OBJECT(v); if (!OBJECT_IS_XML(cx, vobj)) { *bp = JS_FALSE; } else { vxml = (JSXML *) JS_GetPrivate(cx, vobj); if (!XMLEquals(cx, xml, vxml, bp)) return JS_FALSE; } } return JS_TRUE; } static JSBool CheckCycle(JSContext *cx, JSXML *xml, JSXML *kid) { JS_ASSERT(kid->xml_class != JSXML_CLASS_LIST); do { if (xml == kid) { JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_CYCLIC_VALUE, js_XML_str); return JS_FALSE; } } while ((xml = xml->parent) != NULL); return JS_TRUE; } /* ECMA-357 9.1.1.11 XML [[Insert]]. */ static JSBool Insert(JSContext *cx, JSXML *xml, uint32 i, jsval v) { uint32 j, n; JSXML *vxml, *kid; JSObject *vobj; JSString *str; if (!JSXML_HAS_KIDS(xml)) return JS_TRUE; n = 1; vxml = NULL; if (!JSVAL_IS_PRIMITIVE(v)) { vobj = JSVAL_TO_OBJECT(v); if (OBJECT_IS_XML(cx, vobj)) { vxml = (JSXML *) JS_GetPrivate(cx, vobj); if (vxml->xml_class == JSXML_CLASS_LIST) { n = vxml->xml_kids.length; if (n == 0) return JS_TRUE; for (j = 0; j < n; j++) { kid = XMLARRAY_MEMBER(&vxml->xml_kids, j, JSXML); if (!kid) continue; if (!CheckCycle(cx, xml, kid)) return JS_FALSE; } } else if (vxml->xml_class == JSXML_CLASS_ELEMENT) { /* OPTION: enforce that descendants have superset namespaces. */ if (!CheckCycle(cx, xml, vxml)) return JS_FALSE; } } } if (!vxml) { str = js_ValueToString(cx, v); if (!str) return JS_FALSE; vxml = js_NewXML(cx, JSXML_CLASS_TEXT); if (!vxml) return JS_FALSE; vxml->xml_value = str; } if (i > xml->xml_kids.length) i = xml->xml_kids.length; if (!XMLArrayInsert(cx, &xml->xml_kids, i, n)) return JS_FALSE; if (vxml->xml_class == JSXML_CLASS_LIST) { for (j = 0; j < n; j++) { kid = XMLARRAY_MEMBER(&vxml->xml_kids, j, JSXML); if (!kid) continue; kid->parent = xml; XMLARRAY_SET_MEMBER(&xml->xml_kids, i + j, kid); /* OPTION: enforce that descendants have superset namespaces. */ } } else { vxml->parent = xml; XMLARRAY_SET_MEMBER(&xml->xml_kids, i, vxml); } return JS_TRUE; } static JSBool IndexToIdVal(JSContext *cx, uint32 index, jsval *idvp) { JSString *str; if (index <= JSVAL_INT_MAX) { *idvp = INT_TO_JSVAL(index); } else { str = js_NumberToString(cx, (jsdouble) index); if (!str) return JS_FALSE; *idvp = STRING_TO_JSVAL(str); } return JS_TRUE; } /* ECMA-357 9.1.1.12 XML [[Replace]]. */ static JSBool Replace(JSContext *cx, JSXML *xml, jsval id, jsval v) { uint32 i, n; JSXML *vxml, *kid; JSObject *vobj; jsval junk; JSString *str; if (!JSXML_HAS_KIDS(xml)) return JS_TRUE; if (!js_IdIsIndex(id, &i)) { ReportBadXMLName(cx, id); return JS_FALSE; } /* * 9.1.1.12 * [[Replace]] handles _i >= x.[[Length]]_ by incrementing _x.[[Length]_. * It should therefore constrain callers to pass in _i <= x.[[Length]]_. */ n = xml->xml_kids.length; if (i >= n) { if (!IndexToIdVal(cx, n, &id)) return JS_FALSE; i = n; } vxml = NULL; if (!JSVAL_IS_PRIMITIVE(v)) { vobj = JSVAL_TO_OBJECT(v); if (OBJECT_IS_XML(cx, vobj)) vxml = (JSXML *) JS_GetPrivate(cx, vobj); } switch (vxml ? vxml->xml_class : JSXML_CLASS_LIMIT) { case JSXML_CLASS_ELEMENT: /* OPTION: enforce that descendants have superset namespaces. */ if (!CheckCycle(cx, xml, vxml)) return JS_FALSE; case JSXML_CLASS_COMMENT: case JSXML_CLASS_PROCESSING_INSTRUCTION: case JSXML_CLASS_TEXT: goto do_replace; case JSXML_CLASS_LIST: if (i < n && !DeleteByIndex(cx, xml, id, &junk)) return JS_FALSE; if (!Insert(cx, xml, i, v)) return JS_FALSE; break; default: str = js_ValueToString(cx, v); if (!str) return JS_FALSE; vxml = js_NewXML(cx, JSXML_CLASS_TEXT); if (!vxml) return JS_FALSE; vxml->xml_value = str; do_replace: vxml->parent = xml; if (i < n) { kid = XMLARRAY_MEMBER(&xml->xml_kids, i, JSXML); if (kid) kid->parent = NULL; } if (!XMLARRAY_ADD_MEMBER(cx, &xml->xml_kids, i, vxml)) return JS_FALSE; break; } return JS_TRUE; } /* Forward declared -- its implementation uses other statics that call it. */ static JSBool ResolveValue(JSContext *cx, JSXML *list, JSXML **result); /* ECMA-357 9.1.1.3 XML [[Delete]], 9.2.1.3 XML [[Delete]]. */ static JSBool DeleteProperty(JSContext *cx, JSObject *obj, jsval id, jsval *vp) { JSXML *xml, *kid, *parent; JSBool isIndex; JSXMLArray *array; uint32 length, index, kidIndex, deleteCount; JSXMLQName *nameqn; jsid funid; JSObject *nameobj, *kidobj; JSXMLNameMatcher matcher; xml = (JSXML *) JS_GetPrivate(cx, obj); isIndex = js_IdIsIndex(id, &index); if (JSXML_HAS_KIDS(xml)) { array = &xml->xml_kids; length = array->length; } else { array = NULL; length = 0; } if (xml->xml_class == JSXML_CLASS_LIST) { /* ECMA-357 9.2.1.3. */ if (isIndex && index < length) { kid = XMLARRAY_MEMBER(array, index, JSXML); if (!kid) goto out; parent = kid->parent; if (parent) { JS_ASSERT(parent != xml); JS_ASSERT(JSXML_HAS_KIDS(parent)); if (kid->xml_class == JSXML_CLASS_ATTRIBUTE) { nameqn = kid->name; nameobj = js_GetAttributeNameObject(cx, nameqn); if (!nameobj || !js_GetXMLObject(cx, parent)) return JS_FALSE; id = OBJECT_TO_JSVAL(nameobj); if (!DeleteProperty(cx, parent->object, id, vp)) return JS_FALSE; } else { kidIndex = XMLARRAY_FIND_MEMBER(&parent->xml_kids, kid, NULL); JS_ASSERT(kidIndex != XML_NOT_FOUND); if (!IndexToIdVal(cx, kidIndex, &id)) return JS_FALSE; if (!DeleteByIndex(cx, parent, id, vp)) return JS_FALSE; } } XMLArrayDelete(cx, array, index, JS_TRUE); } else { for (index = 0; index < length; index++) { kid = XMLARRAY_MEMBER(array, index, JSXML); if (kid && kid->xml_class == JSXML_CLASS_ELEMENT) { kidobj = js_GetXMLObject(cx, kid); if (!kidobj || !DeleteProperty(cx, kidobj, id, vp)) return JS_FALSE; } } } } else { /* ECMA-357 9.1.1.3. */ if (isIndex) { /* See NOTE in spec: this variation is reserved for future use. */ ReportBadXMLName(cx, id); return JS_FALSE; } nameqn = ToXMLName(cx, id, &funid); if (!nameqn) return JS_FALSE; if (funid) goto out; nameobj = nameqn->object; if (OBJ_GET_CLASS(cx, nameobj) == &js_AttributeNameClass) { if (xml->xml_class != JSXML_CLASS_ELEMENT) goto out; array = &xml->xml_attrs; length = array->length; matcher = MatchAttrName; } else { matcher = MatchElemName; } if (length != 0) { deleteCount = 0; for (index = 0; index < length; index++) { kid = XMLARRAY_MEMBER(array, index, JSXML); if (kid && matcher(nameqn, kid)) { kid->parent = NULL; XMLArrayDelete(cx, array, index, JS_FALSE); ++deleteCount; } else if (deleteCount != 0) { XMLARRAY_SET_MEMBER(array, index - deleteCount, array->vector[index]); } } array->length -= deleteCount; } } out: *vp = JSVAL_TRUE; return JS_TRUE; } static JSBool SyncInScopeNamespaces(JSContext *cx, JSXML *xml) { JSXMLArray *nsarray; uint32 i, n; JSXMLNamespace *ns; nsarray = &xml->xml_namespaces; while ((xml = xml->parent) != NULL) { for (i = 0, n = xml->xml_namespaces.length; i < n; i++) { ns = XMLARRAY_MEMBER(&xml->xml_namespaces, i, JSXMLNamespace); if (ns && !XMLARRAY_HAS_MEMBER(nsarray, ns, namespace_identity)) { if (!XMLARRAY_APPEND(cx, nsarray, ns)) return JS_FALSE; } } } return JS_TRUE; } static JSBool GetNamedProperty(JSContext *cx, JSXML *xml, JSXMLQName* nameqn, JSBool attributes, JSXML *list) { JSXMLArray *array; JSXMLNameMatcher matcher; JSXMLArrayCursor cursor; JSXML *kid; JSBool ok; if (!JSXML_HAS_KIDS(xml)) return JS_TRUE; if (attributes) { array = &xml->xml_attrs; matcher = MatchAttrName; } else { array = &xml->xml_kids; matcher = MatchElemName; } XMLArrayCursorInit(&cursor, array); while ((kid = (JSXML *) XMLArrayCursorNext(&cursor)) != NULL) { if (matcher(nameqn, kid)) { if (!attributes && kid->xml_class == JSXML_CLASS_ELEMENT) { ok = SyncInScopeNamespaces(cx, kid); if (!ok) goto out; } ok = Append(cx, list, kid); if (!ok) goto out; } } ok = JS_TRUE; out: XMLArrayCursorFinish(&cursor); return ok; } /* ECMA-357 9.1.1.1 XML [[Get]] and 9.2.1.1 XMLList [[Get]]. */ static JSBool GetProperty(JSContext *cx, JSObject *obj, jsval id, jsval *vp) { JSXML *xml, *list, *kid; uint32 index; JSObject *kidobj, *listobj; JSXMLQName *nameqn; jsid funid; jsval roots[2]; JSTempValueRooter tvr; JSBool attributes; JSXMLArrayCursor cursor; xml = (JSXML *) JS_GetInstancePrivate(cx, obj, &js_XMLClass, NULL); if (!xml) return JS_TRUE; if (js_IdIsIndex(id, &index)) { if (xml->xml_class != JSXML_CLASS_LIST) { *vp = (index == 0) ? OBJECT_TO_JSVAL(obj) : JSVAL_VOID; } else { /* * ECMA-357 9.2.1.1 starts here. * * Erratum: 9.2 is not completely clear that indexed properties * correspond to kids, but that's what it seems to say, and it's * what any sane user would want. */ if (index < xml->xml_kids.length) { kid = XMLARRAY_MEMBER(&xml->xml_kids, index, JSXML); if (!kid) { *vp = JSVAL_VOID; return JS_TRUE; } kidobj = js_GetXMLObject(cx, kid); if (!kidobj) return JS_FALSE; *vp = OBJECT_TO_JSVAL(kidobj); } else { *vp = JSVAL_VOID; } } return JS_TRUE; } /* * ECMA-357 9.2.1.1/9.1.1.1 qname case. */ nameqn = ToXMLName(cx, id, &funid); if (!nameqn) return JS_FALSE; if (funid) return js_GetXMLFunction(cx, obj, funid, vp); roots[0] = OBJECT_TO_JSVAL(nameqn->object); JS_PUSH_TEMP_ROOT(cx, 1, roots, &tvr); listobj = js_NewXMLObject(cx, JSXML_CLASS_LIST); if (listobj) { roots[1] = OBJECT_TO_JSVAL(listobj); tvr.count++; list = (JSXML *) JS_GetPrivate(cx, listobj); attributes = (OBJ_GET_CLASS(cx, nameqn->object) == &js_AttributeNameClass); if (xml->xml_class == JSXML_CLASS_LIST) { XMLArrayCursorInit(&cursor, &xml->xml_kids); while ((kid = (JSXML *) XMLArrayCursorNext(&cursor)) != NULL) { if (kid->xml_class == JSXML_CLASS_ELEMENT && !GetNamedProperty(cx, kid, nameqn, attributes, list)) { listobj = NULL; break; } } XMLArrayCursorFinish(&cursor); } else { if (!GetNamedProperty(cx, xml, nameqn, attributes, list)) listobj = NULL; } /* * Erratum: ECMA-357 9.1.1.1 misses that [[Append]] sets the given * list's [[TargetProperty]] to the property that is being appended. * This means that any use of the internal [[Get]] property returns * a list which, when used by e.g. [[Insert]] duplicates the last * element matched by id. * See bug 336921. */ list->xml_target = xml; list->xml_targetprop = nameqn; *vp = OBJECT_TO_JSVAL(listobj); } JS_POP_TEMP_ROOT(cx, &tvr); return listobj != NULL; } static JSXML * CopyOnWrite(JSContext *cx, JSXML *xml, JSObject *obj) { JS_ASSERT(xml->object != obj); xml = DeepCopy(cx, xml, obj, 0); if (!xml) return NULL; JS_ASSERT(xml->object == obj); return xml; } #define CHECK_COPY_ON_WRITE(cx,xml,obj) \ (xml->object == obj ? xml : CopyOnWrite(cx, xml, obj)) static JSString * KidToString(JSContext *cx, JSXML *xml, uint32 index) { JSXML *kid; JSObject *kidobj; kid = XMLARRAY_MEMBER(&xml->xml_kids, index, JSXML); if (!kid) return cx->runtime->emptyString; kidobj = js_GetXMLObject(cx, kid); if (!kidobj) return NULL; return js_ValueToString(cx, OBJECT_TO_JSVAL(kidobj)); } /* ECMA-357 9.1.1.2 XML [[Put]] and 9.2.1.2 XMLList [[Put]]. */ static JSBool PutProperty(JSContext *cx, JSObject *obj, jsval id, jsval *vp) { JSBool ok, primitiveAssign; enum { OBJ_ROOT, ID_ROOT, VAL_ROOT }; jsval roots[3]; JSTempValueRooter tvr; JSXML *xml, *vxml, *rxml, *kid, *attr, *parent, *copy, *kid2, *match; JSObject *vobj, *nameobj, *attrobj, *parentobj, *kidobj, *copyobj; JSXMLQName *targetprop, *nameqn, *attrqn; uint32 index, i, j, k, n, q; jsval attrval, nsval, junk; jsid funid; JSString *left, *right, *space; JSXMLNamespace *ns; xml = (JSXML *) JS_GetInstancePrivate(cx, obj, &js_XMLClass, NULL); if (!xml) return JS_TRUE; xml = CHECK_COPY_ON_WRITE(cx, xml, obj); if (!xml) return JS_FALSE; /* Precompute vxml for 9.2.1.2 2(c)(vii)(2-3) and 2(d) and 9.1.1.2 1. */ vxml = NULL; if (!JSVAL_IS_PRIMITIVE(*vp)) { vobj = JSVAL_TO_OBJECT(*vp); if (OBJECT_IS_XML(cx, vobj)) vxml = (JSXML *) JS_GetPrivate(cx, vobj); } /* Control flow after here must exit via label out. */ ok = js_EnterLocalRootScope(cx); if (!ok) return JS_FALSE; roots[OBJ_ROOT] = OBJECT_TO_JSVAL(obj); roots[ID_ROOT] = id; roots[VAL_ROOT] = *vp; JS_PUSH_TEMP_ROOT(cx, 3, roots, &tvr); if (xml->xml_class == JSXML_CLASS_LIST) { /* ECMA-357 9.2.1.2. */ if (js_IdIsIndex(id, &index)) { /* Step 1 sets i to the property index. */ i = index; /* 2(a-b). */ if (xml->xml_target) { ok = ResolveValue(cx, xml->xml_target, &rxml); if (!ok) goto out; if (!rxml) goto out; JS_ASSERT(rxml->object); } else { rxml = NULL; } /* 2(c). */ if (index >= xml->xml_kids.length) { /* 2(c)(i). */ if (rxml) { if (rxml->xml_class == JSXML_CLASS_LIST) { if (rxml->xml_kids.length != 1) goto out; rxml = XMLARRAY_MEMBER(&rxml->xml_kids, 0, JSXML); if (!rxml) goto out; ok = js_GetXMLObject(cx, rxml) != NULL; if (!ok) goto out; } /* * Erratum: ECMA-357 9.2.1.2 step 2(c)(ii) sets * _y.[[Parent]] = r_ where _r_ is the result of * [[ResolveValue]] called on _x.[[TargetObject]] in * 2(a)(i). This can result in text parenting text: * * var MYXML = new XML(); * MYXML.appendChild(new XML("Giants")); * * (testcase from Werner Sharp ). * * To match insertChildAfter, insertChildBefore, * prependChild, and setChildren, we should silently * do nothing in this case. */ if (!JSXML_HAS_KIDS(rxml)) goto out; } /* 2(c)(ii) is distributed below as several js_NewXML calls. */ targetprop = xml->xml_targetprop; if (!targetprop || IS_STAR(targetprop->localName)) { /* 2(c)(iv)(1-2), out of order w.r.t. 2(c)(iii). */ kid = js_NewXML(cx, JSXML_CLASS_TEXT); if (!kid) goto bad; } else { nameobj = js_GetXMLQNameObject(cx, targetprop); if (!nameobj) goto bad; if (OBJ_GET_CLASS(cx, nameobj) == &js_AttributeNameClass) { /* * 2(c)(iii)(1-3). * Note that rxml can't be null here, because target * and targetprop are non-null. */ ok = GetProperty(cx, rxml->object, id, &attrval); if (!ok) goto out; if (JSVAL_IS_PRIMITIVE(attrval)) /* no such attribute */ goto out; attrobj = JSVAL_TO_OBJECT(attrval); attr = (JSXML *) JS_GetPrivate(cx, attrobj); if (JSXML_LENGTH(attr) != 0) goto out; kid = js_NewXML(cx, JSXML_CLASS_ATTRIBUTE); } else { /* 2(c)(v). */ kid = js_NewXML(cx, JSXML_CLASS_ELEMENT); } if (!kid) goto bad; /* An important bit of 2(c)(ii). */ kid->name = targetprop; } /* Final important bit of 2(c)(ii). */ kid->parent = rxml; /* 2(c)(vi-vii). */ i = xml->xml_kids.length; if (kid->xml_class != JSXML_CLASS_ATTRIBUTE) { /* * 2(c)(vii)(1) tests whether _y.[[Parent]]_ is not null. * y.[[Parent]] is here called kid->parent, which we know * from 2(c)(ii) is _r_, here called rxml. So let's just * test that! Erratum, the spec should be simpler here. */ if (rxml) { JS_ASSERT(JSXML_HAS_KIDS(rxml)); n = rxml->xml_kids.length; j = n - 1; if (n != 0 && i != 0) { for (n = j, j = 0; j < n; j++) { if (rxml->xml_kids.vector[j] == xml->xml_kids.vector[i-1]) { break; } } } kidobj = js_GetXMLObject(cx, kid); if (!kidobj) goto bad; ok = Insert(cx, rxml, j + 1, OBJECT_TO_JSVAL(kidobj)); if (!ok) goto out; } /* * 2(c)(vii)(2-3). * Erratum: [[PropertyName]] in 2(c)(vii)(3) must be a * typo for [[TargetProperty]]. */ if (vxml) { kid->name = (vxml->xml_class == JSXML_CLASS_LIST) ? vxml->xml_targetprop : vxml->name; } } /* 2(c)(viii). */ ok = Append(cx, xml, kid); if (!ok) goto out; } /* 2(d). */ if (!vxml || vxml->xml_class == JSXML_CLASS_TEXT || vxml->xml_class == JSXML_CLASS_ATTRIBUTE) { ok = JS_ConvertValue(cx, *vp, JSTYPE_STRING, vp); if (!ok) goto out; roots[VAL_ROOT] = *vp; } /* 2(e). */ kid = XMLARRAY_MEMBER(&xml->xml_kids, i, JSXML); if (!kid) goto out; parent = kid->parent; if (kid->xml_class == JSXML_CLASS_ATTRIBUTE) { nameobj = js_GetAttributeNameObject(cx, kid->name); if (!nameobj) goto bad; id = OBJECT_TO_JSVAL(nameobj); if (parent) { /* 2(e)(i). */ parentobj = js_GetXMLObject(cx, parent); if (!parentobj) goto bad; ok = PutProperty(cx, parentobj, id, vp); if (!ok) goto out; /* 2(e)(ii). */ ok = GetProperty(cx, parentobj, id, vp); if (!ok) goto out; attr = (JSXML *) JS_GetPrivate(cx, JSVAL_TO_OBJECT(*vp)); /* 2(e)(iii). */ xml->xml_kids.vector[i] = attr->xml_kids.vector[0]; } } /* 2(f). */ else if (vxml && vxml->xml_class == JSXML_CLASS_LIST) { /* 2(f)(i) Create a shallow copy _c_ of _V_. */ copyobj = js_NewXMLObject(cx, JSXML_CLASS_LIST); if (!copyobj) goto bad; copy = (JSXML *) JS_GetPrivate(cx, copyobj); n = vxml->xml_kids.length; ok = XMLArraySetCapacity(cx, ©->xml_kids, n); if (!ok) goto out; for (k = 0; k < n; k++) { kid2 = XMLARRAY_MEMBER(&vxml->xml_kids, k, JSXML); XMLARRAY_SET_MEMBER(©->xml_kids, k, kid2); } JS_ASSERT(parent != xml); if (parent) { q = XMLARRAY_FIND_MEMBER(&parent->xml_kids, kid, NULL); JS_ASSERT(q != XML_NOT_FOUND); ok = IndexToIdVal(cx, q, &id); if (!ok) goto out; ok = Replace(cx, parent, id, OBJECT_TO_JSVAL(copyobj)); if (!ok) goto out; #ifdef DEBUG /* Erratum: this loop in the spec is useless. */ for (j = 0, n = copy->xml_kids.length; j < n; j++) { kid2 = XMLARRAY_MEMBER(&parent->xml_kids, q + j, JSXML); JS_ASSERT(XMLARRAY_MEMBER(©->xml_kids, j, JSXML) == kid2); } #endif } /* * 2(f)(iv-vi). * Erratum: notice the unhandled zero-length V basis case and * the off-by-one errors for the n != 0 cases in the spec. */ if (n == 0) { XMLArrayDelete(cx, &xml->xml_kids, i, JS_TRUE); } else { ok = XMLArrayInsert(cx, &xml->xml_kids, i + 1, n - 1); if (!ok) goto out; for (j = 0; j < n; j++) xml->xml_kids.vector[i + j] = copy->xml_kids.vector[j]; } } /* 2(g). */ else if (vxml || JSXML_HAS_VALUE(kid)) { if (parent) { q = XMLARRAY_FIND_MEMBER(&parent->xml_kids, kid, NULL); JS_ASSERT(q != XML_NOT_FOUND); ok = IndexToIdVal(cx, q, &id); if (!ok) goto out; ok = Replace(cx, parent, id, *vp); if (!ok) goto out; vxml = XMLARRAY_MEMBER(&parent->xml_kids, q, JSXML); if (!vxml) goto out; roots[VAL_ROOT] = *vp = OBJECT_TO_JSVAL(vxml->object); } /* * 2(g)(iii). * Erratum: _V_ may not be of type XML, but all index-named * properties _x[i]_ in an XMLList _x_ must be of type XML, * according to 9.2.1.1 Overview and other places in the spec. * * Thanks to 2(d), we know _V_ (*vp here) is either a string * or an XML/XMLList object. If *vp is a string, call ToXML * on it to satisfy the constraint. */ if (!vxml) { JS_ASSERT(JSVAL_IS_STRING(*vp)); vobj = ToXML(cx, *vp); if (!vobj) goto bad; roots[VAL_ROOT] = *vp = OBJECT_TO_JSVAL(vobj); vxml = (JSXML *) JS_GetPrivate(cx, vobj); } XMLARRAY_SET_MEMBER(&xml->xml_kids, i, vxml); } /* 2(h). */ else { kidobj = js_GetXMLObject(cx, kid); if (!kidobj) goto bad; id = ATOM_KEY(cx->runtime->atomState.starAtom); ok = PutProperty(cx, kidobj, id, vp); if (!ok) goto out; } } else { /* * 3. * Erratum: if x.[[Length]] > 1 or [[ResolveValue]] returns null * or an r with r.[[Length]] != 1, throw TypeError. */ n = JSXML_LENGTH(xml); if (n > 1) goto type_error; if (n == 0) { ok = ResolveValue(cx, xml, &rxml); if (!ok) goto out; if (!rxml || JSXML_LENGTH(rxml) != 1) goto type_error; ok = Append(cx, xml, rxml); if (!ok) goto out; } JS_ASSERT(JSXML_LENGTH(xml) == 1); kid = XMLARRAY_MEMBER(&xml->xml_kids, 0, JSXML); if (!kid) goto out; kidobj = js_GetXMLObject(cx, kid); if (!kidobj) goto bad; ok = PutProperty(cx, kidobj, id, vp); if (!ok) goto out; } } else { /* * ECMA-357 9.1.1.2. * Erratum: move steps 3 and 4 to before 1 and 2, to avoid wasted * effort in ToString or [[DeepCopy]]. */ if (js_IdIsIndex(id, &index)) { /* See NOTE in spec: this variation is reserved for future use. */ ReportBadXMLName(cx, id); goto bad; } nameqn = ToXMLName(cx, id, &funid); if (!nameqn) goto bad; if (funid) { ok = js_SetProperty(cx, obj, funid, vp); goto out; } nameobj = nameqn->object; if (JSXML_HAS_VALUE(xml)) goto out; if (!vxml || vxml->xml_class == JSXML_CLASS_TEXT || vxml->xml_class == JSXML_CLASS_ATTRIBUTE) { ok = JS_ConvertValue(cx, *vp, JSTYPE_STRING, vp); if (!ok) goto out; } else { rxml = DeepCopyInLRS(cx, vxml, 0); if (!rxml || !js_GetXMLObject(cx, rxml)) goto bad; vxml = rxml; *vp = OBJECT_TO_JSVAL(vxml->object); } roots[VAL_ROOT] = *vp; /* * 6. * Erratum: why is this done here, so early? use is way later.... */ ok = js_GetDefaultXMLNamespace(cx, &nsval); if (!ok) goto out; if (OBJ_GET_CLASS(cx, nameobj) == &js_AttributeNameClass) { /* 7(a). */ if (!js_IsXMLName(cx, OBJECT_TO_JSVAL(nameobj))) goto out; /* 7(b-c). */ if (vxml && vxml->xml_class == JSXML_CLASS_LIST) { n = vxml->xml_kids.length; if (n == 0) { *vp = STRING_TO_JSVAL(cx->runtime->emptyString); } else { left = KidToString(cx, vxml, 0); if (!left) goto bad; space = ATOM_TO_STRING(cx->runtime->atomState.spaceAtom); for (i = 1; i < n; i++) { left = js_ConcatStrings(cx, left, space); if (!left) goto bad; right = KidToString(cx, vxml, i); if (!right) goto bad; left = js_ConcatStrings(cx, left, right); if (!left) goto bad; } roots[VAL_ROOT] = *vp = STRING_TO_JSVAL(left); } } else { ok = JS_ConvertValue(cx, *vp, JSTYPE_STRING, vp); if (!ok) goto out; roots[VAL_ROOT] = *vp; } /* 7(d-e). */ match = NULL; for (i = 0, n = xml->xml_attrs.length; i < n; i++) { attr = XMLARRAY_MEMBER(&xml->xml_attrs, i, JSXML); if (!attr) continue; attrqn = attr->name; if (js_EqualStrings(attrqn->localName, nameqn->localName) && (!nameqn->uri || js_EqualStrings(attrqn->uri, nameqn->uri))) { if (!match) { match = attr; } else { nameobj = js_GetAttributeNameObject(cx, attrqn); if (!nameobj) goto bad; id = OBJECT_TO_JSVAL(nameobj); ok = DeleteProperty(cx, obj, id, &junk); if (!ok) goto out; --i; } } } /* 7(f). */ attr = match; if (!attr) { /* 7(f)(i-ii). */ if (!nameqn->uri) { left = right = cx->runtime->emptyString; } else { left = nameqn->uri; right = nameqn->prefix; } nameqn = js_NewXMLQName(cx, left, right, nameqn->localName); if (!nameqn) goto bad; /* 7(f)(iii). */ attr = js_NewXML(cx, JSXML_CLASS_ATTRIBUTE); if (!attr) goto bad; attr->parent = xml; attr->name = nameqn; /* 7(f)(iv). */ ok = XMLARRAY_ADD_MEMBER(cx, &xml->xml_attrs, n, attr); if (!ok) goto out; /* 7(f)(v-vi). */ ns = GetNamespace(cx, nameqn, NULL); if (!ns) goto bad; ok = AddInScopeNamespace(cx, xml, ns); if (!ok) goto out; } /* 7(g). */ attr->xml_value = JSVAL_TO_STRING(*vp); goto out; } /* 8-9. */ if (!js_IsXMLName(cx, OBJECT_TO_JSVAL(nameobj)) && !IS_STAR(nameqn->localName)) { goto out; } /* 10-11. */ id = JSVAL_VOID; primitiveAssign = !vxml && !IS_STAR(nameqn->localName); /* 12. */ k = n = xml->xml_kids.length; kid2 = NULL; while (k != 0) { --k; kid = XMLARRAY_MEMBER(&xml->xml_kids, k, JSXML); if (kid && MatchElemName(nameqn, kid)) { if (!JSVAL_IS_VOID(id)) { ok = DeleteByIndex(cx, xml, id, &junk); if (!ok) goto out; } ok = IndexToIdVal(cx, k, &id); if (!ok) goto out; kid2 = kid; } } /* * Erratum: ECMA-357 specified child insertion inconsistently: * insertChildBefore and insertChildAfter insert an arbitrary XML * instance, and therefore can create cycles, but appendChild as * specified by the "Overview" of 13.4.4.3 calls [[DeepCopy]] on * its argument. But the "Semantics" in 13.4.4.3 do not include * any [[DeepCopy]] call. * * Fixing this (https://bugzilla.mozilla.org/show_bug.cgi?id=312692) * required adding cycle detection, and allowing duplicate kids to * be created (see comment 6 in the bug). Allowing duplicate kid * references means the loop above will delete all but the lowest * indexed reference, and each [[DeleteByIndex]] nulls the kid's * parent. Thus the need to restore parent here. This is covered * by https://bugzilla.mozilla.org/show_bug.cgi?id=327564. */ if (kid2) { JS_ASSERT(kid2->parent == xml || !kid2->parent); if (!kid2->parent) kid2->parent = xml; } /* 13. */ if (JSVAL_IS_VOID(id)) { /* 13(a). */ ok = IndexToIdVal(cx, n, &id); if (!ok) goto out; /* 13(b). */ if (primitiveAssign) { if (!nameqn->uri) { ns = (JSXMLNamespace *) JS_GetPrivate(cx, JSVAL_TO_OBJECT(nsval)); left = ns->uri; right = ns->prefix; } else { left = nameqn->uri; right = nameqn->prefix; } nameqn = js_NewXMLQName(cx, left, right, nameqn->localName); if (!nameqn) goto bad; /* 13(b)(iii). */ vobj = js_NewXMLObject(cx, JSXML_CLASS_ELEMENT); if (!vobj) goto bad; vxml = (JSXML *) JS_GetPrivate(cx, vobj); vxml->parent = xml; vxml->name = nameqn; /* 13(b)(iv-vi). */ ns = GetNamespace(cx, nameqn, NULL); if (!ns) goto bad; ok = Replace(cx, xml, id, OBJECT_TO_JSVAL(vobj)); if (!ok) goto out; ok = AddInScopeNamespace(cx, vxml, ns); if (!ok) goto out; } } /* 14. */ if (primitiveAssign) { JSXMLArrayCursor cursor; js_IdIsIndex(id, &index); XMLArrayCursorInit(&cursor, &xml->xml_kids); cursor.index = index; kid = (JSXML *) XMLArrayCursorItem(&cursor); if (JSXML_HAS_KIDS(kid)) { XMLArrayFinish(cx, &kid->xml_kids); ok = XMLArrayInit(cx, &kid->xml_kids, 1); } /* 14(b-c). */ /* XXXbe Erratum? redundant w.r.t. 7(b-c) else clause above */ if (ok) { ok = JS_ConvertValue(cx, *vp, JSTYPE_STRING, vp); if (ok && !IS_EMPTY(JSVAL_TO_STRING(*vp))) { roots[VAL_ROOT] = *vp; if ((JSXML *) XMLArrayCursorItem(&cursor) == kid) ok = Replace(cx, kid, JSVAL_ZERO, *vp); } } XMLArrayCursorFinish(&cursor); } else { /* 15(a). */ ok = Replace(cx, xml, id, *vp); } } out: JS_POP_TEMP_ROOT(cx, &tvr); js_LeaveLocalRootScope(cx); return ok; type_error: JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_BAD_XMLLIST_PUT, js_ValueToPrintableString(cx, id)); bad: ok = JS_FALSE; goto out; } /* ECMA-357 9.1.1.10 XML [[ResolveValue]], 9.2.1.10 XMLList [[ResolveValue]]. */ static JSBool ResolveValue(JSContext *cx, JSXML *list, JSXML **result) { JSXML *target, *base; JSXMLQName *targetprop; JSObject *targetpropobj; jsval id, tv; /* Our caller must be protecting newborn objects. */ JS_ASSERT(cx->localRootStack); if (list->xml_class != JSXML_CLASS_LIST || list->xml_kids.length != 0) { if (!js_GetXMLObject(cx, list)) return JS_FALSE; *result = list; return JS_TRUE; } target = list->xml_target; targetprop = list->xml_targetprop; if (!target || !targetprop || IS_STAR(targetprop->localName)) { *result = NULL; return JS_TRUE; } targetpropobj = js_GetXMLQNameObject(cx, targetprop); if (!targetpropobj) return JS_FALSE; if (OBJ_GET_CLASS(cx, targetpropobj) == &js_AttributeNameClass) { *result = NULL; return JS_TRUE; } if (!ResolveValue(cx, target, &base)) return JS_FALSE; if (!base) { *result = NULL; return JS_TRUE; } if (!js_GetXMLObject(cx, base)) return JS_FALSE; id = OBJECT_TO_JSVAL(targetpropobj); if (!GetProperty(cx, base->object, id, &tv)) return JS_FALSE; target = (JSXML *) JS_GetPrivate(cx, JSVAL_TO_OBJECT(tv)); if (JSXML_LENGTH(target) == 0) { if (base->xml_class == JSXML_CLASS_LIST && JSXML_LENGTH(base) > 1) { *result = NULL; return JS_TRUE; } tv = STRING_TO_JSVAL(cx->runtime->emptyString); if (!PutProperty(cx, base->object, id, &tv)) return JS_FALSE; if (!GetProperty(cx, base->object, id, &tv)) return JS_FALSE; target = (JSXML *) JS_GetPrivate(cx, JSVAL_TO_OBJECT(tv)); } *result = target; return JS_TRUE; } /* * HasProperty must be able to return a found JSProperty and the object in * which it was found, if id is of the form function::name. For other ids, * if they index or name an XML child, we return FOUND_XML_PROPERTY in *propp * and null in *objp. * * DROP_PROPERTY helps HasProperty callers drop function properties without * trying to drop the magic FOUND_XML_PROPERTY cookie. */ #define FOUND_XML_PROPERTY ((JSProperty *) 1) #define DROP_PROPERTY(cx,pobj,prop) (((prop) != FOUND_XML_PROPERTY) \ ? OBJ_DROP_PROPERTY(cx, pobj, prop) \ : (void) 0) /* ECMA-357 9.1.1.6 XML [[HasProperty]] and 9.2.1.5 XMLList [[HasProperty]]. */ static JSBool HasProperty(JSContext *cx, JSObject *obj, jsval id, JSObject **objp, JSProperty **propp) { JSXML *xml, *kid; JSXMLArrayCursor cursor; JSObject *kidobj; JSXMLQName *qn; jsid funid; JSXMLArray *array; JSXMLNameMatcher matcher; uint32 i, n; *objp = NULL; *propp = NULL; xml = (JSXML *) JS_GetPrivate(cx, obj); if (xml->xml_class == JSXML_CLASS_LIST) { n = JSXML_LENGTH(xml); if (js_IdIsIndex(id, &i)) { if (i < n) *propp = FOUND_XML_PROPERTY; return JS_TRUE; } XMLArrayCursorInit(&cursor, &xml->xml_kids); while ((kid = (JSXML *) XMLArrayCursorNext(&cursor)) != NULL) { if (kid->xml_class == JSXML_CLASS_ELEMENT) { kidobj = js_GetXMLObject(cx, kid); if (!kidobj || !HasProperty(cx, kidobj, id, objp, propp)) break; if (*propp) break; } } XMLArrayCursorFinish(&cursor); if (kid) return *propp != NULL; } else { if (xml->xml_class == JSXML_CLASS_ELEMENT && js_IdIsIndex(id, &i)) { if (i == 0) *propp = FOUND_XML_PROPERTY; return JS_TRUE; } qn = ToXMLName(cx, id, &funid); if (!qn) return JS_FALSE; if (funid) return js_LookupProperty(cx, obj, funid, objp, propp); if (xml->xml_class != JSXML_CLASS_ELEMENT) return JS_TRUE; if (OBJ_GET_CLASS(cx, qn->object) == &js_AttributeNameClass) { array = &xml->xml_attrs; matcher = MatchAttrName; } else { array = &xml->xml_kids; matcher = MatchElemName; } for (i = 0, n = array->length; i < n; i++) { kid = XMLARRAY_MEMBER(array, i, JSXML); if (kid && matcher(qn, kid)) { *propp = FOUND_XML_PROPERTY; return JS_TRUE; } } } return JS_TRUE; } static void xml_finalize(JSContext *cx, JSObject *obj) { JSXML *xml; xml = (JSXML *) JS_GetPrivate(cx, obj); if (!xml) return; if (xml->object == obj) xml->object = NULL; UNMETER(xml_stats.livexmlobj); } static void xml_mark_vector(JSContext *cx, JSXML **vec, uint32 len) { uint32 i; JSXML *elt; for (i = 0; i < len; i++) { elt = vec[i]; { #ifdef GC_MARK_DEBUG char buf[120]; if (elt->xml_class == JSXML_CLASS_LIST) { strcpy(buf, js_XMLList_str); } else if (JSXML_HAS_NAME(elt)) { JSXMLQName *qn = elt->name; JS_snprintf(buf, sizeof buf, "%s::%s", qn->uri ? JS_GetStringBytes(qn->uri) : "*", JS_GetStringBytes(qn->localName)); } else { JSString *str = elt->xml_value; size_t srclen = JSSTRING_LENGTH(str); size_t dstlen = sizeof buf; if (srclen >= sizeof buf / 6) srclen = sizeof buf / 6 - 1; js_DeflateStringToBuffer(cx, JSSTRING_CHARS(str), srclen, buf, &dstlen); } #endif GC_MARK(cx, elt, buf); } } } /* * js_XMLObjectOps.newObjectMap == js_NewObjectMap, so XML objects appear to * be native. Therefore, xml_lookupProperty must return a valid JSProperty * pointer parameter via *propp to signify "property found". Since the only * call to xml_lookupProperty is via OBJ_LOOKUP_PROPERTY, and then only from * js_FindXMLProperty (in this file), js_FindProperty (in jsobj.c, called from * jsinterp.c) or from JSOP_IN case in the interpreter, the only time we add a * JSScopeProperty here is when an unqualified name or XML name is being * accessed or when "name in xml" is called. * * This scope property keeps the JSOP_NAME code in js_Interpret happy by * giving it an sprop with (getter, setter) == (GetProperty, PutProperty). * * NB: xml_deleteProperty must take care to remove any property added here. * * FIXME This clashes with the function namespace implementation which also * uses native properties. Effectively after xml_lookupProperty any property * stored previously using assignments to xml.function::name will be removed. * We partially workaround the problem in js_GetXMLFunction. There we take * advantage of the fact that typically function:: is used to access the * functions from XML.prototype. So when js_GetProperty returns a non-function * property, we assume that it represents the result of GetProperty setter * hiding the function and use an extra prototype chain lookup to recover it. * For a proper solution see bug 355257. */ static JSBool xml_lookupProperty(JSContext *cx, JSObject *obj, jsid id, JSObject **objp, JSProperty **propp) { JSScopeProperty *sprop; if (!HasProperty(cx, obj, ID_TO_VALUE(id), objp, propp)) return JS_FALSE; if (*propp == FOUND_XML_PROPERTY) { sprop = js_AddNativeProperty(cx, obj, id, GetProperty, PutProperty, SPROP_INVALID_SLOT, JSPROP_ENUMERATE, 0, 0); if (!sprop) return JS_FALSE; JS_LOCK_OBJ(cx, obj); *objp = obj; *propp = (JSProperty *) sprop; } return JS_TRUE; } static JSBool xml_defineProperty(JSContext *cx, JSObject *obj, jsid id, jsval value, JSPropertyOp getter, JSPropertyOp setter, uintN attrs, JSProperty **propp) { if (VALUE_IS_FUNCTION(cx, value) || getter || setter || (attrs & JSPROP_ENUMERATE) == 0 || (attrs & (JSPROP_READONLY | JSPROP_PERMANENT | JSPROP_SHARED))) { return js_DefineProperty(cx, obj, id, value, getter, setter, attrs, propp); } if (!PutProperty(cx, obj, ID_TO_VALUE(id), &value)) return JS_FALSE; if (propp) *propp = NULL; return JS_TRUE; } static JSBool xml_getProperty(JSContext *cx, JSObject *obj, jsid id, jsval *vp) { if (id == JS_DEFAULT_XML_NAMESPACE_ID) { *vp = JSVAL_VOID; return JS_TRUE; } return GetProperty(cx, obj, ID_TO_VALUE(id), vp); } static JSBool xml_setProperty(JSContext *cx, JSObject *obj, jsid id, jsval *vp) { return PutProperty(cx, obj, ID_TO_VALUE(id), vp); } static JSBool FoundProperty(JSContext *cx, JSObject *obj, jsid id, JSProperty *prop, JSBool *foundp) { JSObject *pobj; if (prop) { *foundp = JS_TRUE; } else { if (!HasProperty(cx, obj, ID_TO_VALUE(id), &pobj, &prop)) return JS_FALSE; if (prop) DROP_PROPERTY(cx, pobj, prop); *foundp = (prop != NULL); } return JS_TRUE; } static JSBool xml_getAttributes(JSContext *cx, JSObject *obj, jsid id, JSProperty *prop, uintN *attrsp) { JSBool found; if (!FoundProperty(cx, obj, id, prop, &found)) return JS_FALSE; *attrsp = found ? JSPROP_ENUMERATE : 0; return JS_TRUE; } static JSBool xml_setAttributes(JSContext *cx, JSObject *obj, jsid id, JSProperty *prop, uintN *attrsp) { JSBool found; if (!FoundProperty(cx, obj, id, prop, &found)) return JS_FALSE; if (found) { JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_CANT_SET_XML_ATTRS); } return !found; } static JSBool xml_deleteProperty(JSContext *cx, JSObject *obj, jsid id, jsval *rval) { /* * If this object has its own (mutable) scope, and if id isn't an index, * then we may have added a property to the scope in xml_lookupProperty * for it to return to mean "found" and to provide a handle for access * operations to call the property's getter or setter. The property also * helps speed up unqualified accesses via the property cache, avoiding * what amount to two HasProperty searches. * * But now it's time to remove any such property, to purge the property * cache and remove the scope entry. */ if (OBJ_SCOPE(obj)->object == obj && !JSID_IS_INT(id)) { if (!js_DeleteProperty(cx, obj, id, rval)) return JS_FALSE; } return DeleteProperty(cx, obj, ID_TO_VALUE(id), rval); } static JSBool xml_defaultValue(JSContext *cx, JSObject *obj, JSType hint, jsval *vp) { JSXML *xml; if (hint == JSTYPE_OBJECT) { /* Called from for..in code in js_Interpret: return an XMLList. */ xml = (JSXML *) JS_GetPrivate(cx, obj); if (xml->xml_class != JSXML_CLASS_LIST) { obj = ToXMLList(cx, OBJECT_TO_JSVAL(obj)); if (!obj) return JS_FALSE; } *vp = OBJECT_TO_JSVAL(obj); return JS_TRUE; } return JS_CallFunctionName(cx, obj, js_toString_str, 0, NULL, vp); } static JSBool xml_enumerate(JSContext *cx, JSObject *obj, JSIterateOp enum_op, jsval *statep, jsid *idp) { JSXML *xml; uint32 length, index; JSXMLArrayCursor *cursor; xml = (JSXML *) JS_GetPrivate(cx, obj); length = JSXML_LENGTH(xml); switch (enum_op) { case JSENUMERATE_INIT: if (length == 0) { cursor = NULL; } else { cursor = (JSXMLArrayCursor *) JS_malloc(cx, sizeof *cursor); if (!cursor) return JS_FALSE; XMLArrayCursorInit(cursor, &xml->xml_kids); } *statep = PRIVATE_TO_JSVAL(cursor); if (idp) *idp = INT_TO_JSID(length); break; case JSENUMERATE_NEXT: cursor = JSVAL_TO_PRIVATE(*statep); if (cursor && cursor->array && (index = cursor->index) < length) { *idp = INT_TO_JSID(index); cursor->index = index + 1; break; } /* FALL THROUGH */ case JSENUMERATE_DESTROY: cursor = JSVAL_TO_PRIVATE(*statep); if (cursor) { XMLArrayCursorFinish(cursor); JS_free(cx, cursor); } *statep = JSVAL_NULL; break; } return JS_TRUE; } static JSBool xml_hasInstance(JSContext *cx, JSObject *obj, jsval v, JSBool *bp) { return JS_TRUE; } static uint32 xml_mark(JSContext *cx, JSObject *obj, void *arg) { JSXML *xml; xml = (JSXML *) JS_GetPrivate(cx, obj); GC_MARK(cx, xml, "private"); return js_Mark(cx, obj, NULL); } static void xml_clear(JSContext *cx, JSObject *obj) { } static JSBool HasSimpleContent(JSXML *xml) { JSXML *kid; JSBool simple; uint32 i, n; again: switch (xml->xml_class) { case JSXML_CLASS_COMMENT: case JSXML_CLASS_PROCESSING_INSTRUCTION: return JS_FALSE; case JSXML_CLASS_LIST: if (xml->xml_kids.length == 0) return JS_TRUE; if (xml->xml_kids.length == 1) { kid = XMLARRAY_MEMBER(&xml->xml_kids, 0, JSXML); if (kid) { xml = kid; goto again; } } /* FALL THROUGH */ default: simple = JS_TRUE; for (i = 0, n = JSXML_LENGTH(xml); i < n; i++) { kid = XMLARRAY_MEMBER(&xml->xml_kids, i, JSXML); if (kid && kid->xml_class == JSXML_CLASS_ELEMENT) { simple = JS_FALSE; break; } } return simple; } } /* * 11.2.2.1 Step 3(d) onward. */ static JSObject * xml_getMethod(JSContext *cx, JSObject *obj, jsid id, jsval *vp) { JSTempValueRooter tvr; JS_ASSERT(JS_InstanceOf(cx, obj, &js_XMLClass, NULL)); /* * As our callers have a bad habit of passing a pointer to an unrooted * local value as vp, we use a proper root here. */ JS_PUSH_SINGLE_TEMP_ROOT(cx, JSVAL_NULL, &tvr); if (!js_GetXMLFunction(cx, obj, id, &tvr.u.value)) obj = NULL; *vp = tvr.u.value; JS_POP_TEMP_ROOT(cx, &tvr); return obj; } static JSBool xml_setMethod(JSContext *cx, JSObject *obj, jsid id, jsval *vp) { return js_SetProperty(cx, obj, id, vp); } static JSBool xml_enumerateValues(JSContext *cx, JSObject *obj, JSIterateOp enum_op, jsval *statep, jsid *idp, jsval *vp) { JSXML *xml, *kid; uint32 length, index; JSXMLArrayCursor *cursor; JSObject *kidobj; xml = (JSXML *) JS_GetPrivate(cx, obj); length = JSXML_LENGTH(xml); JS_ASSERT(INT_FITS_IN_JSVAL(length)); switch (enum_op) { case JSENUMERATE_INIT: if (length == 0) { cursor = NULL; } else { cursor = (JSXMLArrayCursor *) JS_malloc(cx, sizeof *cursor); if (!cursor) return JS_FALSE; XMLArrayCursorInit(cursor, &xml->xml_kids); } *statep = PRIVATE_TO_JSVAL(cursor); if (idp) *idp = INT_TO_JSID(length); if (vp) *vp = JSVAL_VOID; break; case JSENUMERATE_NEXT: cursor = JSVAL_TO_PRIVATE(*statep); if (cursor && cursor->array && (index = cursor->index) < length) { while (!(kid = XMLARRAY_MEMBER(&xml->xml_kids, index, JSXML))) { if (++index == length) goto destroy; } kidobj = js_GetXMLObject(cx, kid); if (!kidobj) return JS_FALSE; JS_ASSERT(INT_FITS_IN_JSVAL(index)); *idp = INT_TO_JSID(index); *vp = OBJECT_TO_JSVAL(kidobj); cursor->index = index + 1; break; } /* FALL THROUGH */ case JSENUMERATE_DESTROY: cursor = JSVAL_TO_PRIVATE(*statep); if (cursor) { destroy: XMLArrayCursorFinish(cursor); JS_free(cx, cursor); } *statep = JSVAL_NULL; break; } return JS_TRUE; } static JSBool xml_equality(JSContext *cx, JSObject *obj, jsval v, JSBool *bp) { JSXML *xml, *vxml; JSObject *vobj; JSBool ok; JSString *str, *vstr; jsdouble d, d2; xml = (JSXML *) JS_GetPrivate(cx, obj); vxml = NULL; if (!JSVAL_IS_PRIMITIVE(v)) { vobj = JSVAL_TO_OBJECT(v); if (OBJECT_IS_XML(cx, vobj)) vxml = (JSXML *) JS_GetPrivate(cx, vobj); } if (xml->xml_class == JSXML_CLASS_LIST) { ok = Equals(cx, xml, v, bp); } else if (vxml) { if (vxml->xml_class == JSXML_CLASS_LIST) { ok = Equals(cx, vxml, OBJECT_TO_JSVAL(obj), bp); } else { if (((xml->xml_class == JSXML_CLASS_TEXT || xml->xml_class == JSXML_CLASS_ATTRIBUTE) && HasSimpleContent(vxml)) || ((vxml->xml_class == JSXML_CLASS_TEXT || vxml->xml_class == JSXML_CLASS_ATTRIBUTE) && HasSimpleContent(xml))) { ok = js_EnterLocalRootScope(cx); if (ok) { str = js_ValueToString(cx, OBJECT_TO_JSVAL(obj)); vstr = js_ValueToString(cx, v); ok = str && vstr; if (ok) *bp = js_EqualStrings(str, vstr); js_LeaveLocalRootScope(cx); } } else { ok = XMLEquals(cx, xml, vxml, bp); } } } else { ok = js_EnterLocalRootScope(cx); if (ok) { if (HasSimpleContent(xml)) { str = js_ValueToString(cx, OBJECT_TO_JSVAL(obj)); vstr = js_ValueToString(cx, v); ok = str && vstr; if (ok) *bp = js_EqualStrings(str, vstr); } else if (JSVAL_IS_STRING(v) || JSVAL_IS_NUMBER(v)) { str = js_ValueToString(cx, OBJECT_TO_JSVAL(obj)); if (!str) { ok = JS_FALSE; } else if (JSVAL_IS_STRING(v)) { *bp = js_EqualStrings(str, JSVAL_TO_STRING(v)); } else { ok = js_ValueToNumber(cx, STRING_TO_JSVAL(str), &d); if (ok) { d2 = JSVAL_IS_INT(v) ? JSVAL_TO_INT(v) : *JSVAL_TO_DOUBLE(v); *bp = JSDOUBLE_COMPARE(d, ==, d2, JS_FALSE); } } } else { *bp = JS_FALSE; } js_LeaveLocalRootScope(cx); } } return ok; } static JSBool xml_concatenate(JSContext *cx, JSObject *obj, jsval v, jsval *vp) { JSBool ok; JSObject *listobj, *robj; JSXML *list, *lxml, *rxml; ok = js_EnterLocalRootScope(cx); if (!ok) return JS_FALSE; listobj = js_NewXMLObject(cx, JSXML_CLASS_LIST); if (!listobj) { ok = JS_FALSE; goto out; } list = (JSXML *) JS_GetPrivate(cx, listobj); lxml = (JSXML *) JS_GetPrivate(cx, obj); ok = Append(cx, list, lxml); if (!ok) goto out; if (VALUE_IS_XML(cx, v)) { rxml = (JSXML *) JS_GetPrivate(cx, JSVAL_TO_OBJECT(v)); } else { robj = ToXML(cx, v); if (!robj) { ok = JS_FALSE; goto out; } rxml = (JSXML *) JS_GetPrivate(cx, robj); } ok = Append(cx, list, rxml); if (!ok) goto out; *vp = OBJECT_TO_JSVAL(listobj); out: js_LeaveLocalRootScopeWithResult(cx, *vp); return ok; } /* Use js_NewObjectMap so XML objects satisfy OBJ_IS_NATIVE tests. */ JS_FRIEND_DATA(JSXMLObjectOps) js_XMLObjectOps = { { js_NewObjectMap, js_DestroyObjectMap, xml_lookupProperty, xml_defineProperty, xml_getProperty, xml_setProperty, xml_getAttributes, xml_setAttributes, xml_deleteProperty, xml_defaultValue, xml_enumerate, js_CheckAccess, NULL, NULL, NULL, NULL, NULL, xml_hasInstance, js_SetProtoOrParent, js_SetProtoOrParent, xml_mark, xml_clear, NULL, NULL }, xml_getMethod, xml_setMethod, xml_enumerateValues, xml_equality, xml_concatenate }; static JSObjectOps * xml_getObjectOps(JSContext *cx, JSClass *clasp) { return &js_XMLObjectOps.base; } JS_FRIEND_DATA(JSClass) js_XMLClass = { js_XML_str, JSCLASS_HAS_PRIVATE | JSCLASS_HAS_CACHED_PROTO(JSProto_XML), JS_PropertyStub, JS_PropertyStub, JS_PropertyStub, JS_PropertyStub, JS_EnumerateStub, JS_ResolveStub, JS_ConvertStub, xml_finalize, xml_getObjectOps, NULL, NULL, NULL, NULL, NULL, NULL, NULL }; static JSObject * CallConstructorFunction(JSContext *cx, JSObject *obj, JSClass *clasp, uintN argc, jsval *argv) { JSObject *tmp; jsval rval; while ((tmp = OBJ_GET_PARENT(cx, obj)) != NULL) obj = tmp; if (!JS_CallFunctionName(cx, obj, clasp->name, argc, argv, &rval)) return NULL; JS_ASSERT(!JSVAL_IS_PRIMITIVE(rval)); return JSVAL_TO_OBJECT(rval); } static JSXML * StartNonListXMLMethod(JSContext *cx, JSObject **objp, jsval *argv) { JSXML *xml; JSFunction *fun; JS_ASSERT(VALUE_IS_FUNCTION(cx, argv[-2])); xml = (JSXML *) JS_GetInstancePrivate(cx, *objp, &js_XMLClass, argv); if (!xml || xml->xml_class != JSXML_CLASS_LIST) return xml; if (xml->xml_kids.length == 1) { xml = XMLARRAY_MEMBER(&xml->xml_kids, 0, JSXML); if (xml) { *objp = js_GetXMLObject(cx, xml); if (!*objp) return NULL; argv[-1] = OBJECT_TO_JSVAL(*objp); return xml; } } fun = (JSFunction *) JS_GetPrivate(cx, JSVAL_TO_OBJECT(argv[-2])); if (fun) { char numBuf[12]; JS_snprintf(numBuf, sizeof numBuf, "%u", xml->xml_kids.length); JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_NON_LIST_XML_METHOD, JS_GetFunctionName(fun), numBuf); } return NULL; } #define XML_METHOD_PROLOG \ JS_BEGIN_MACRO \ xml = (JSXML *) JS_GetInstancePrivate(cx, obj, &js_XMLClass, argv); \ if (!xml) \ return JS_FALSE; \ JS_END_MACRO #define NON_LIST_XML_METHOD_PROLOG \ JS_BEGIN_MACRO \ xml = StartNonListXMLMethod(cx, &obj, argv); \ if (!xml) \ return JS_FALSE; \ JS_ASSERT(xml->xml_class != JSXML_CLASS_LIST); \ JS_END_MACRO static JSBool xml_addNamespace(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) { JSXML *xml; JSObject *nsobj; JSXMLNamespace *ns; NON_LIST_XML_METHOD_PROLOG; if (xml->xml_class != JSXML_CLASS_ELEMENT) return JS_TRUE; xml = CHECK_COPY_ON_WRITE(cx, xml, obj); if (!xml) return JS_FALSE; nsobj = CallConstructorFunction(cx, obj, &js_NamespaceClass.base, 1, argv); if (!nsobj) return JS_FALSE; argv[0] = OBJECT_TO_JSVAL(nsobj); ns = (JSXMLNamespace *) JS_GetPrivate(cx, nsobj); if (!AddInScopeNamespace(cx, xml, ns)) return JS_FALSE; ns->declared = JS_TRUE; *rval = OBJECT_TO_JSVAL(obj); return JS_TRUE; } static JSBool xml_appendChild(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) { JSXML *xml, *vxml; jsval name, v; JSObject *vobj; NON_LIST_XML_METHOD_PROLOG; xml = CHECK_COPY_ON_WRITE(cx, xml, obj); if (!xml) return JS_FALSE; if (!js_GetAnyName(cx, &name)) return JS_FALSE; if (!GetProperty(cx, obj, name, &v)) return JS_FALSE; JS_ASSERT(!JSVAL_IS_PRIMITIVE(v)); vobj = JSVAL_TO_OBJECT(v); JS_ASSERT(OBJECT_IS_XML(cx, vobj)); vxml = (JSXML *) JS_GetPrivate(cx, vobj); JS_ASSERT(vxml->xml_class == JSXML_CLASS_LIST); if (!IndexToIdVal(cx, vxml->xml_kids.length, &name)) return JS_FALSE; if (!PutProperty(cx, JSVAL_TO_OBJECT(v), name, &argv[0])) return JS_FALSE; *rval = OBJECT_TO_JSVAL(obj); return JS_TRUE; } /* XML and XMLList */ static JSBool xml_attribute(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) { JSXMLQName *qn; qn = ToAttributeName(cx, argv[0]); if (!qn) return JS_FALSE; argv[0] = OBJECT_TO_JSVAL(qn->object); /* local root */ return GetProperty(cx, obj, argv[0], rval); } /* XML and XMLList */ static JSBool xml_attributes(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) { jsval name; JSXMLQName *qn; JSTempValueRooter tvr; JSBool ok; name = ATOM_KEY(cx->runtime->atomState.starAtom); qn = ToAttributeName(cx, name); if (!qn) return JS_FALSE; name = OBJECT_TO_JSVAL(qn->object); JS_PUSH_SINGLE_TEMP_ROOT(cx, name, &tvr); ok = GetProperty(cx, obj, name, rval); JS_POP_TEMP_ROOT(cx, &tvr); return ok; } static JSXML * xml_list_helper(JSContext *cx, JSXML *xml, jsval *rval) { JSObject *listobj; JSXML *list; listobj = js_NewXMLObject(cx, JSXML_CLASS_LIST); if (!listobj) return NULL; *rval = OBJECT_TO_JSVAL(listobj); list = (JSXML *) JS_GetPrivate(cx, listobj); list->xml_target = xml; return list; } static JSBool xml_child_helper(JSContext *cx, JSObject *obj, JSXML *xml, jsval name, jsval *rval) { uint32 index; JSXML *kid; JSObject *kidobj; /* ECMA-357 13.4.4.6 */ JS_ASSERT(xml->xml_class != JSXML_CLASS_LIST); if (js_IdIsIndex(name, &index)) { if (index >= JSXML_LENGTH(xml)) { *rval = JSVAL_VOID; } else { kid = XMLARRAY_MEMBER(&xml->xml_kids, index, JSXML); if (!kid) { *rval = JSVAL_VOID; } else { kidobj = js_GetXMLObject(cx, kid); if (!kidobj) return JS_FALSE; *rval = OBJECT_TO_JSVAL(kidobj); } } return JS_TRUE; } return GetProperty(cx, obj, name, rval); } /* XML and XMLList */ static JSBool xml_child(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) { JSXML *xml, *list, *kid, *vxml; JSXMLArrayCursor cursor; jsval name, v; JSObject *kidobj; XML_METHOD_PROLOG; name = argv[0]; if (xml->xml_class == JSXML_CLASS_LIST) { /* ECMA-357 13.5.4.4 */ list = xml_list_helper(cx, xml, rval); if (!list) return JS_FALSE; XMLArrayCursorInit(&cursor, &xml->xml_kids); while ((kid = (JSXML *) XMLArrayCursorNext(&cursor)) != NULL) { kidobj = js_GetXMLObject(cx, kid); if (!kidobj) break; if (!xml_child_helper(cx, kidobj, kid, name, &v)) break; if (JSVAL_IS_VOID(v)) { /* The property didn't exist in this kid. */ continue; } JS_ASSERT(!JSVAL_IS_PRIMITIVE(v)); vxml = (JSXML *) JS_GetPrivate(cx, JSVAL_TO_OBJECT(v)); if ((!JSXML_HAS_KIDS(vxml) || vxml->xml_kids.length != 0) && !Append(cx, list, vxml)) { break; } } XMLArrayCursorFinish(&cursor); return !kid; } /* ECMA-357 Edition 2 13.3.4.6 (note 13.3, not 13.4 as in Edition 1). */ if (!xml_child_helper(cx, obj, xml, name, rval)) return JS_FALSE; if (JSVAL_IS_VOID(*rval) && !xml_list_helper(cx, xml, rval)) return JS_FALSE; return JS_TRUE; } static JSBool xml_childIndex(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) { JSXML *xml, *parent; uint32 i, n; NON_LIST_XML_METHOD_PROLOG; parent = xml->parent; if (!parent || xml->xml_class == JSXML_CLASS_ATTRIBUTE) { *rval = DOUBLE_TO_JSVAL(cx->runtime->jsNaN); return JS_TRUE; } for (i = 0, n = JSXML_LENGTH(parent); i < n; i++) { if (XMLARRAY_MEMBER(&parent->xml_kids, i, JSXML) == xml) break; } JS_ASSERT(i < n); return js_NewNumberValue(cx, i, rval); } /* XML and XMLList */ static JSBool xml_children(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) { jsval name; name = ATOM_KEY(cx->runtime->atomState.starAtom); return GetProperty(cx, obj, name, rval); } /* XML and XMLList */ static JSBool xml_comments(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) { JSXML *xml, *list, *kid, *vxml; JSBool ok; uint32 i, n; JSObject *kidobj; jsval v; XML_METHOD_PROLOG; list = xml_list_helper(cx, xml, rval); if (!list) return JS_FALSE; ok = JS_TRUE; if (xml->xml_class == JSXML_CLASS_LIST) { /* 13.5.4.6 Step 2. */ for (i = 0, n = JSXML_LENGTH(xml); i < n; i++) { kid = XMLARRAY_MEMBER(&xml->xml_kids, i, JSXML); if (kid && kid->xml_class == JSXML_CLASS_ELEMENT) { ok = js_EnterLocalRootScope(cx); if (!ok) break; kidobj = js_GetXMLObject(cx, kid); if (kidobj) { ok = xml_comments(cx, kidobj, argc, argv, &v); } else { ok = JS_FALSE; v = JSVAL_NULL; } js_LeaveLocalRootScopeWithResult(cx, v); if (!ok) break; vxml = (JSXML *) JS_GetPrivate(cx, JSVAL_TO_OBJECT(v)); if (JSXML_LENGTH(vxml) != 0) { ok = Append(cx, list, vxml); if (!ok) break; } } } } else { /* 13.4.4.9 Step 2. */ for (i = 0, n = JSXML_LENGTH(xml); i < n; i++) { kid = XMLARRAY_MEMBER(&xml->xml_kids, i, JSXML); if (kid && kid->xml_class == JSXML_CLASS_COMMENT) { ok = Append(cx, list, kid); if (!ok) break; } } } return ok; } /* XML and XMLList */ static JSBool xml_contains(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) { JSXML *xml, *kid; jsval value; JSBool eq; JSXMLArrayCursor cursor; JSObject *kidobj; XML_METHOD_PROLOG; value = argv[0]; if (xml->xml_class == JSXML_CLASS_LIST) { eq = JS_FALSE; XMLArrayCursorInit(&cursor, &xml->xml_kids); while ((kid = (JSXML *) XMLArrayCursorNext(&cursor)) != NULL) { kidobj = js_GetXMLObject(cx, kid); if (!kidobj || !xml_equality(cx, kidobj, value, &eq)) break; if (eq) break; } XMLArrayCursorFinish(&cursor); if (kid && !eq) return JS_FALSE; } else { if (!xml_equality(cx, obj, value, &eq)) return JS_FALSE; } *rval = BOOLEAN_TO_JSVAL(eq); return JS_TRUE; } /* XML and XMLList */ static JSBool xml_copy(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) { JSXML *xml, *copy; XML_METHOD_PROLOG; copy = DeepCopy(cx, xml, NULL, 0); if (!copy) return JS_FALSE; *rval = OBJECT_TO_JSVAL(copy->object); return JS_TRUE; } /* XML and XMLList */ static JSBool xml_descendants(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) { JSXML *xml, *list; jsval name; XML_METHOD_PROLOG; name = (argc == 0) ? ATOM_KEY(cx->runtime->atomState.starAtom) : argv[0]; list = Descendants(cx, xml, name); if (!list) return JS_FALSE; *rval = OBJECT_TO_JSVAL(list->object); return JS_TRUE; } /* XML and XMLList */ static JSBool xml_elements(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) { JSXML *xml, *list, *kid, *vxml; jsval name, v; JSXMLQName *nameqn; jsid funid; JSBool ok; JSXMLArrayCursor cursor; JSObject *kidobj; uint32 i, n; XML_METHOD_PROLOG; name = (argc == 0) ? ATOM_KEY(cx->runtime->atomState.starAtom) : argv[0]; nameqn = ToXMLName(cx, name, &funid); if (!nameqn) return JS_FALSE; argv[0] = OBJECT_TO_JSVAL(nameqn->object); list = xml_list_helper(cx, xml, rval); if (!list) return JS_FALSE; if (funid) return JS_TRUE; list->xml_targetprop = nameqn; ok = JS_TRUE; if (xml->xml_class == JSXML_CLASS_LIST) { /* 13.5.4.6 */ XMLArrayCursorInit(&cursor, &xml->xml_kids); while ((kid = (JSXML *) XMLArrayCursorNext(&cursor)) != NULL) { if (kid->xml_class == JSXML_CLASS_ELEMENT) { ok = js_EnterLocalRootScope(cx); if (!ok) break; kidobj = js_GetXMLObject(cx, kid); if (kidobj) { ok = xml_elements(cx, kidobj, argc, argv, &v); } else { ok = JS_FALSE; v = JSVAL_NULL; } js_LeaveLocalRootScopeWithResult(cx, v); if (!ok) break; vxml = (JSXML *) JS_GetPrivate(cx, JSVAL_TO_OBJECT(v)); if (JSXML_LENGTH(vxml) != 0) { ok = Append(cx, list, vxml); if (!ok) break; } } } XMLArrayCursorFinish(&cursor); } else { for (i = 0, n = JSXML_LENGTH(xml); i < n; i++) { kid = XMLARRAY_MEMBER(&xml->xml_kids, i, JSXML); if (kid && kid->xml_class == JSXML_CLASS_ELEMENT && MatchElemName(nameqn, kid)) { ok = Append(cx, list, kid); if (!ok) break; } } } return ok; } /* XML and XMLList */ static JSBool xml_hasOwnProperty(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) { jsval name; JSObject *pobj; JSProperty *prop; if (!JS_InstanceOf(cx, obj, &js_XMLClass, argv)) return JS_FALSE; name = argv[0]; if (!HasProperty(cx, obj, name, &pobj, &prop)) return JS_FALSE; if (!prop) { return js_HasOwnPropertyHelper(cx, obj, js_LookupProperty, argc, argv, rval); } DROP_PROPERTY(cx, pobj, prop); *rval = JSVAL_TRUE; return JS_TRUE; } /* XML and XMLList */ static JSBool xml_hasComplexContent(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) { JSXML *xml, *kid; JSObject *kidobj; uint32 i, n; XML_METHOD_PROLOG; again: switch (xml->xml_class) { case JSXML_CLASS_ATTRIBUTE: case JSXML_CLASS_COMMENT: case JSXML_CLASS_PROCESSING_INSTRUCTION: case JSXML_CLASS_TEXT: *rval = JSVAL_FALSE; break; case JSXML_CLASS_LIST: if (xml->xml_kids.length == 0) { *rval = JSVAL_TRUE; } else if (xml->xml_kids.length == 1) { kid = XMLARRAY_MEMBER(&xml->xml_kids, 0, JSXML); if (kid) { kidobj = js_GetXMLObject(cx, kid); if (!kidobj) return JS_FALSE; obj = kidobj; xml = (JSXML *) JS_GetPrivate(cx, obj); goto again; } } /* FALL THROUGH */ default: *rval = JSVAL_FALSE; for (i = 0, n = xml->xml_kids.length; i < n; i++) { kid = XMLARRAY_MEMBER(&xml->xml_kids, i, JSXML); if (kid && kid->xml_class == JSXML_CLASS_ELEMENT) { *rval = JSVAL_TRUE; break; } } break; } return JS_TRUE; } /* XML and XMLList */ static JSBool xml_hasSimpleContent(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) { JSXML *xml; XML_METHOD_PROLOG; *rval = BOOLEAN_TO_JSVAL(HasSimpleContent(xml)); return JS_TRUE; } typedef struct JSTempRootedNSArray { JSTempValueRooter tvr; JSXMLArray array; jsval value; /* extra root for temporaries */ } JSTempRootedNSArray; JS_STATIC_DLL_CALLBACK(void) mark_temp_ns_array(JSContext *cx, JSTempValueRooter *tvr) { JSTempRootedNSArray *tmp = (JSTempRootedNSArray *)tvr; namespace_mark_vector(cx, (JSXMLNamespace **)tmp->array.vector, tmp->array.length); XMLArrayCursorMark(cx, tmp->array.cursors); if (JSVAL_IS_GCTHING(tmp->value)) GC_MARK(cx, JSVAL_TO_GCTHING(tmp->value), "temp_ns_array_value"); } static void InitTempNSArray(JSContext *cx, JSTempRootedNSArray *tmp) { XMLArrayInit(cx, &tmp->array, 0); tmp->value = JSVAL_NULL; JS_PUSH_TEMP_ROOT_MARKER(cx, mark_temp_ns_array, &tmp->tvr); } static void FinishTempNSArray(JSContext *cx, JSTempRootedNSArray *tmp) { JS_ASSERT(tmp->tvr.u.marker == mark_temp_ns_array); JS_POP_TEMP_ROOT(cx, &tmp->tvr); XMLArrayFinish(cx, &tmp->array); } /* * Populate a new JS array with elements of JSTempRootedNSArray.array and * place the result into rval. rval must point to a rooted location. */ static JSBool TempNSArrayToJSArray(JSContext *cx, JSTempRootedNSArray *tmp, jsval *rval) { JSObject *arrayobj; uint32 i, n; JSXMLNamespace *ns; JSObject *nsobj; arrayobj = js_NewArrayObject(cx, 0, NULL); if (!arrayobj) return JS_FALSE; *rval = OBJECT_TO_JSVAL(arrayobj); for (i = 0, n = tmp->array.length; i < n; i++) { ns = XMLARRAY_MEMBER(&tmp->array, i, JSXMLNamespace); if (!ns) continue; nsobj = js_GetXMLNamespaceObject(cx, ns); if (!nsobj) return JS_FALSE; tmp->value = OBJECT_TO_JSVAL(nsobj); if (!OBJ_SET_PROPERTY(cx, arrayobj, INT_TO_JSID(i), &tmp->value)) return JS_FALSE; } return JS_TRUE; } static JSBool FindInScopeNamespaces(JSContext *cx, JSXML *xml, JSXMLArray *nsarray) { uint32 length, i, j, n; JSXMLNamespace *ns, *ns2; length = nsarray->length; do { if (xml->xml_class != JSXML_CLASS_ELEMENT) continue; for (i = 0, n = xml->xml_namespaces.length; i < n; i++) { ns = XMLARRAY_MEMBER(&xml->xml_namespaces, i, JSXMLNamespace); if (!ns) continue; for (j = 0; j < length; j++) { ns2 = XMLARRAY_MEMBER(nsarray, j, JSXMLNamespace); if (ns2 && ((ns2->prefix && ns->prefix) ? js_EqualStrings(ns2->prefix, ns->prefix) : js_EqualStrings(ns2->uri, ns->uri))) { break; } } if (j == length) { if (!XMLARRAY_APPEND(cx, nsarray, ns)) return JS_FALSE; ++length; } } } while ((xml = xml->parent) != NULL); JS_ASSERT(length == nsarray->length); return JS_TRUE; } static JSBool xml_inScopeNamespaces(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) { JSXML *xml; JSTempRootedNSArray namespaces; JSBool ok; NON_LIST_XML_METHOD_PROLOG; InitTempNSArray(cx, &namespaces); ok = FindInScopeNamespaces(cx, xml, &namespaces.array) && TempNSArrayToJSArray(cx, &namespaces, rval); FinishTempNSArray(cx, &namespaces); return ok; } static JSBool xml_insertChildAfter(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) { JSXML *xml, *kid; jsval arg; uint32 i; NON_LIST_XML_METHOD_PROLOG; if (!JSXML_HAS_KIDS(xml)) return JS_TRUE; arg = argv[0]; if (JSVAL_IS_NULL(arg)) { kid = NULL; i = 0; } else { if (!VALUE_IS_XML(cx, arg)) return JS_TRUE; kid = (JSXML *) JS_GetPrivate(cx, JSVAL_TO_OBJECT(arg)); i = XMLARRAY_FIND_MEMBER(&xml->xml_kids, kid, NULL); if (i == XML_NOT_FOUND) return JS_TRUE; ++i; } xml = CHECK_COPY_ON_WRITE(cx, xml, obj); if (!xml) return JS_FALSE; if (!Insert(cx, xml, i, argv[1])) return JS_FALSE; *rval = OBJECT_TO_JSVAL(obj); return JS_TRUE; } static JSBool xml_insertChildBefore(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) { JSXML *xml, *kid; jsval arg; uint32 i; NON_LIST_XML_METHOD_PROLOG; if (!JSXML_HAS_KIDS(xml)) return JS_TRUE; arg = argv[0]; if (JSVAL_IS_NULL(arg)) { kid = NULL; i = xml->xml_kids.length; } else { if (!VALUE_IS_XML(cx, arg)) return JS_TRUE; kid = (JSXML *) JS_GetPrivate(cx, JSVAL_TO_OBJECT(arg)); i = XMLARRAY_FIND_MEMBER(&xml->xml_kids, kid, NULL); if (i == XML_NOT_FOUND) return JS_TRUE; } xml = CHECK_COPY_ON_WRITE(cx, xml, obj); if (!xml) return JS_FALSE; if (!Insert(cx, xml, i, argv[1])) return JS_FALSE; *rval = OBJECT_TO_JSVAL(obj); return JS_TRUE; } /* XML and XMLList */ static JSBool xml_length(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) { JSXML *xml; XML_METHOD_PROLOG; if (xml->xml_class != JSXML_CLASS_LIST) { *rval = JSVAL_ONE; } else { if (!js_NewNumberValue(cx, xml->xml_kids.length, rval)) return JS_FALSE; } return JS_TRUE; } static JSBool xml_localName(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) { JSXML *xml; NON_LIST_XML_METHOD_PROLOG; *rval = xml->name ? STRING_TO_JSVAL(xml->name->localName) : JSVAL_NULL; return JS_TRUE; } static JSBool xml_name(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) { JSXML *xml; JSObject *nameobj; NON_LIST_XML_METHOD_PROLOG; if (!xml->name) { *rval = JSVAL_NULL; } else { nameobj = js_GetXMLQNameObject(cx, xml->name); if (!nameobj) return JS_FALSE; *rval = OBJECT_TO_JSVAL(nameobj); } return JS_TRUE; } static JSBool xml_namespace(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) { JSXML *xml; JSString *prefix; JSTempRootedNSArray inScopeNSes; JSBool ok; jsuint i, length; JSXMLNamespace *ns; JSObject *nsobj; NON_LIST_XML_METHOD_PROLOG; if (argc == 0 && !JSXML_HAS_NAME(xml)) { *rval = JSVAL_NULL; return JS_TRUE; } if (argc == 0) { prefix = NULL; } else { prefix = js_ValueToString(cx, argv[0]); if (!prefix) return JS_FALSE; argv[0] = STRING_TO_JSVAL(prefix); /* local root */ } /* After this point the control must flow through label out. */ InitTempNSArray(cx, &inScopeNSes); ok = FindInScopeNamespaces(cx, xml, &inScopeNSes.array); if (!ok) goto out; if (!prefix) { ns = GetNamespace(cx, xml->name, &inScopeNSes.array); if (!ns) { ok = JS_FALSE; goto out; } } else { ns = NULL; for (i = 0, length = inScopeNSes.array.length; i < length; i++) { ns = XMLARRAY_MEMBER(&inScopeNSes.array, i, JSXMLNamespace); if (ns && ns->prefix && js_EqualStrings(ns->prefix, prefix)) break; ns = NULL; } } if (!ns) { *rval = JSVAL_VOID; } else { nsobj = js_GetXMLNamespaceObject(cx, ns); if (!nsobj) { ok = JS_FALSE; goto out; } *rval = OBJECT_TO_JSVAL(nsobj); } out: FinishTempNSArray(cx, &inScopeNSes); return JS_TRUE; } static JSBool xml_namespaceDeclarations(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) { JSXML *xml, *yml; JSBool ok; JSTempRootedNSArray ancestors, declared; uint32 i, n; JSXMLNamespace *ns; NON_LIST_XML_METHOD_PROLOG; if (JSXML_HAS_VALUE(xml)) return JS_TRUE; /* From here, control flow must goto out to finish these arrays. */ ok = JS_TRUE; InitTempNSArray(cx, &ancestors); InitTempNSArray(cx, &declared); yml = xml; while ((yml = yml->parent) != NULL) { JS_ASSERT(yml->xml_class == JSXML_CLASS_ELEMENT); for (i = 0, n = yml->xml_namespaces.length; i < n; i++) { ns = XMLARRAY_MEMBER(&yml->xml_namespaces, i, JSXMLNamespace); if (ns && !XMLARRAY_HAS_MEMBER(&ancestors.array, ns, namespace_match)) { ok = XMLARRAY_APPEND(cx, &ancestors.array, ns); if (!ok) goto out; } } } for (i = 0, n = xml->xml_namespaces.length; i < n; i++) { ns = XMLARRAY_MEMBER(&xml->xml_namespaces, i, JSXMLNamespace); if (!ns) continue; if (!ns->declared) continue; if (!XMLARRAY_HAS_MEMBER(&ancestors.array, ns, namespace_match)) { ok = XMLARRAY_APPEND(cx, &declared.array, ns); if (!ok) goto out; } } ok = TempNSArrayToJSArray(cx, &declared, rval); out: /* Finishing must be in reverse order of initialization to follow LIFO. */ FinishTempNSArray(cx, &declared); FinishTempNSArray(cx, &ancestors); return ok; } static const char js_attribute_str[] = "attribute"; static const char js_text_str[] = "text"; /* Exported to jsgc.c #ifdef GC_MARK_DEBUG. */ const char *js_xml_class_str[] = { "list", "element", js_attribute_str, "processing-instruction", js_text_str, "comment" }; static JSBool xml_nodeKind(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) { JSXML *xml; JSString *str; NON_LIST_XML_METHOD_PROLOG; str = JS_InternString(cx, js_xml_class_str[xml->xml_class]); if (!str) return JS_FALSE; *rval = STRING_TO_JSVAL(str); return JS_TRUE; } static JSBool NormalizingDelete(JSContext *cx, JSObject *obj, JSXML *xml, jsval id) { jsval junk; if (xml->xml_class == JSXML_CLASS_LIST) return DeleteProperty(cx, obj, id, &junk); return DeleteByIndex(cx, xml, id, &junk); } /* * Erratum? the testcase js/tests/e4x/XML/13.4.4.26.js wants all-whitespace * text between tags to be removed by normalize. */ static JSBool IsXMLSpace(JSString *str) { const jschar *cp, *end; cp = JSSTRING_CHARS(str); end = cp + JSSTRING_LENGTH(str); while (cp < end) { if (!JS_ISXMLSPACE(*cp)) return JS_FALSE; ++cp; } return JS_TRUE; } /* XML and XMLList */ static JSBool xml_normalize(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) { JSXML *xml, *kid, *kid2; uint32 i, n; JSObject *kidobj; JSString *str; jsval junk; XML_METHOD_PROLOG; *rval = OBJECT_TO_JSVAL(obj); if (!JSXML_HAS_KIDS(xml)) return JS_TRUE; xml = CHECK_COPY_ON_WRITE(cx, xml, obj); if (!xml) return JS_FALSE; for (i = 0, n = xml->xml_kids.length; i < n; i++) { kid = XMLARRAY_MEMBER(&xml->xml_kids, i, JSXML); if (!kid) continue; if (kid->xml_class == JSXML_CLASS_ELEMENT) { kidobj = js_GetXMLObject(cx, kid); if (!kidobj || !xml_normalize(cx, kidobj, argc, argv, &junk)) return JS_FALSE; } else if (kid->xml_class == JSXML_CLASS_TEXT) { while (i + 1 < n && (kid2 = XMLARRAY_MEMBER(&xml->xml_kids, i + 1, JSXML)) && kid2->xml_class == JSXML_CLASS_TEXT) { str = js_ConcatStrings(cx, kid->xml_value, kid2->xml_value); if (!str) return JS_FALSE; if (!NormalizingDelete(cx, obj, xml, INT_TO_JSVAL(i + 1))) return JS_FALSE; n = xml->xml_kids.length; kid->xml_value = str; } if (IS_EMPTY(kid->xml_value) || IsXMLSpace(kid->xml_value)) { if (!NormalizingDelete(cx, obj, xml, INT_TO_JSVAL(i))) return JS_FALSE; n = xml->xml_kids.length; --i; } } } return JS_TRUE; } /* XML and XMLList */ static JSBool xml_parent(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) { JSXML *xml, *parent, *kid; uint32 i, n; JSObject *parentobj; XML_METHOD_PROLOG; parent = xml->parent; if (xml->xml_class == JSXML_CLASS_LIST) { *rval = JSVAL_VOID; n = xml->xml_kids.length; if (n == 0) return JS_TRUE; kid = XMLARRAY_MEMBER(&xml->xml_kids, 0, JSXML); if (!kid) return JS_TRUE; parent = kid->parent; for (i = 1; i < n; i++) { kid = XMLARRAY_MEMBER(&xml->xml_kids, i, JSXML); if (kid && kid->parent != parent) return JS_TRUE; } } if (!parent) { *rval = JSVAL_NULL; return JS_TRUE; } parentobj = js_GetXMLObject(cx, parent); if (!parentobj) return JS_FALSE; *rval = OBJECT_TO_JSVAL(parentobj); return JS_TRUE; } /* XML and XMLList */ static JSBool xml_processingInstructions(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) { JSXML *xml, *list, *kid, *vxml; jsval name, v; JSXMLQName *nameqn; jsid funid; JSBool ok; JSXMLArrayCursor cursor; JSObject *kidobj; uint32 i, n; XML_METHOD_PROLOG; name = (argc == 0) ? ATOM_KEY(cx->runtime->atomState.starAtom) : argv[0]; nameqn = ToXMLName(cx, name, &funid); if (!nameqn) return JS_FALSE; argv[0] = OBJECT_TO_JSVAL(nameqn->object); list = xml_list_helper(cx, xml, rval); if (!list) return JS_FALSE; if (funid) return JS_TRUE; list->xml_targetprop = nameqn; ok = JS_TRUE; if (xml->xml_class == JSXML_CLASS_LIST) { /* 13.5.4.17 Step 4 (misnumbered 9 -- Erratum?). */ XMLArrayCursorInit(&cursor, &xml->xml_kids); while ((kid = (JSXML *) XMLArrayCursorNext(&cursor)) != NULL) { if (kid->xml_class == JSXML_CLASS_ELEMENT) { ok = js_EnterLocalRootScope(cx); if (!ok) break; kidobj = js_GetXMLObject(cx, kid); if (kidobj) { ok = xml_processingInstructions(cx, kidobj, argc, argv, &v); } else { ok = JS_FALSE; v = JSVAL_NULL; } js_LeaveLocalRootScopeWithResult(cx, v); if (!ok) break; vxml = (JSXML *) JS_GetPrivate(cx, JSVAL_TO_OBJECT(v)); if (JSXML_LENGTH(vxml) != 0) { ok = Append(cx, list, vxml); if (!ok) break; } } } XMLArrayCursorFinish(&cursor); } else { /* 13.4.4.28 Step 4. */ for (i = 0, n = JSXML_LENGTH(xml); i < n; i++) { kid = XMLARRAY_MEMBER(&xml->xml_kids, i, JSXML); if (kid && kid->xml_class == JSXML_CLASS_PROCESSING_INSTRUCTION && (IS_STAR(nameqn->localName) || js_EqualStrings(nameqn->localName, kid->name->localName))) { ok = Append(cx, list, kid); if (!ok) break; } } } return ok; } static JSBool xml_prependChild(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) { JSXML *xml; NON_LIST_XML_METHOD_PROLOG; xml = CHECK_COPY_ON_WRITE(cx, xml, obj); if (!xml) return JS_FALSE; *rval = OBJECT_TO_JSVAL(obj); return Insert(cx, xml, 0, argv[0]); } /* XML and XMLList */ static JSBool xml_propertyIsEnumerable(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) { JSXML *xml; jsval name; uint32 index; XML_METHOD_PROLOG; name = argv[0]; *rval = JSVAL_FALSE; if (js_IdIsIndex(name, &index)) { if (xml->xml_class == JSXML_CLASS_LIST) { /* 13.5.4.18. */ *rval = BOOLEAN_TO_JSVAL(index < xml->xml_kids.length); } else { /* 13.4.4.30. */ *rval = BOOLEAN_TO_JSVAL(index == 0); } } return JS_TRUE; } static JSBool namespace_full_match(const void *a, const void *b) { const JSXMLNamespace *nsa = (const JSXMLNamespace *) a; const JSXMLNamespace *nsb = (const JSXMLNamespace *) b; if (nsa->prefix && nsb->prefix && !js_EqualStrings(nsa->prefix, nsb->prefix)) { return JS_FALSE; } return js_EqualStrings(nsa->uri, nsb->uri); } static JSBool xml_removeNamespace_helper(JSContext *cx, JSXML *xml, JSXMLNamespace *ns) { JSXMLNamespace *thisns, *attrns; uint32 i, n; JSXML *attr, *kid; thisns = GetNamespace(cx, xml->name, &xml->xml_namespaces); JS_ASSERT(thisns); if (thisns == ns) return JS_TRUE; for (i = 0, n = xml->xml_attrs.length; i < n; i++) { attr = XMLARRAY_MEMBER(&xml->xml_attrs, i, JSXML); if (!attr) continue; attrns = GetNamespace(cx, attr->name, &xml->xml_namespaces); JS_ASSERT(attrns); if (attrns == ns) return JS_TRUE; } i = XMLARRAY_FIND_MEMBER(&xml->xml_namespaces, ns, namespace_full_match); if (i != XML_NOT_FOUND) XMLArrayDelete(cx, &xml->xml_namespaces, i, JS_TRUE); for (i = 0, n = xml->xml_kids.length; i < n; i++) { kid = XMLARRAY_MEMBER(&xml->xml_kids, i, JSXML); if (kid && kid->xml_class == JSXML_CLASS_ELEMENT) { if (!xml_removeNamespace_helper(cx, kid, ns)) return JS_FALSE; } } return JS_TRUE; } static JSBool xml_removeNamespace(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) { JSXML *xml; JSObject *nsobj; JSXMLNamespace *ns; NON_LIST_XML_METHOD_PROLOG; *rval = OBJECT_TO_JSVAL(obj); if (xml->xml_class != JSXML_CLASS_ELEMENT) return JS_TRUE; xml = CHECK_COPY_ON_WRITE(cx, xml, obj); if (!xml) return JS_FALSE; nsobj = CallConstructorFunction(cx, obj, &js_NamespaceClass.base, 1, argv); if (!nsobj) return JS_FALSE; argv[0] = OBJECT_TO_JSVAL(nsobj); ns = (JSXMLNamespace *) JS_GetPrivate(cx, nsobj); /* NOTE: remove ns from each ancestor if not used by that ancestor. */ return xml_removeNamespace_helper(cx, xml, ns); } static JSBool xml_replace(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) { JSXML *xml, *vxml, *kid; jsval name, value, id, junk; uint32 index; JSObject *nameobj; JSXMLQName *nameqn; NON_LIST_XML_METHOD_PROLOG; *rval = OBJECT_TO_JSVAL(obj); if (xml->xml_class != JSXML_CLASS_ELEMENT) return JS_TRUE; value = argv[1]; vxml = VALUE_IS_XML(cx, value) ? (JSXML *) JS_GetPrivate(cx, JSVAL_TO_OBJECT(value)) : NULL; if (!vxml) { if (!JS_ConvertValue(cx, value, JSTYPE_STRING, &argv[1])) return JS_FALSE; value = argv[1]; } else { vxml = DeepCopy(cx, vxml, NULL, 0); if (!vxml) return JS_FALSE; value = argv[1] = OBJECT_TO_JSVAL(vxml->object); } xml = CHECK_COPY_ON_WRITE(cx, xml, obj); if (!xml) return JS_FALSE; name = argv[0]; if (js_IdIsIndex(name, &index)) return Replace(cx, xml, name, value); /* Call function QName per spec, not ToXMLName, to avoid attribute names. */ nameobj = CallConstructorFunction(cx, obj, &js_QNameClass.base, 1, &name); if (!nameobj) return JS_FALSE; argv[0] = OBJECT_TO_JSVAL(nameobj); nameqn = (JSXMLQName *) JS_GetPrivate(cx, nameobj); id = JSVAL_VOID; index = xml->xml_kids.length; while (index != 0) { --index; kid = XMLARRAY_MEMBER(&xml->xml_kids, index, JSXML); if (kid && MatchElemName(nameqn, kid)) { if (!JSVAL_IS_VOID(id) && !DeleteByIndex(cx, xml, id, &junk)) return JS_FALSE; if (!IndexToIdVal(cx, index, &id)) return JS_FALSE; } } if (JSVAL_IS_VOID(id)) return JS_TRUE; return Replace(cx, xml, id, value); } static JSBool xml_setChildren(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) { if (!StartNonListXMLMethod(cx, &obj, argv)) return JS_FALSE; if (!PutProperty(cx, obj, ATOM_KEY(cx->runtime->atomState.starAtom), &argv[0])) { return JS_FALSE; } *rval = OBJECT_TO_JSVAL(obj); return JS_TRUE; } static JSBool xml_setLocalName(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) { JSXML *xml; jsval name; JSXMLQName *nameqn; JSString *namestr; NON_LIST_XML_METHOD_PROLOG; if (!JSXML_HAS_NAME(xml)) return JS_TRUE; name = argv[0]; if (!JSVAL_IS_PRIMITIVE(name) && OBJ_GET_CLASS(cx, JSVAL_TO_OBJECT(name)) == &js_QNameClass.base) { nameqn = (JSXMLQName *) JS_GetPrivate(cx, JSVAL_TO_OBJECT(name)); namestr = nameqn->localName; } else { if (!JS_ConvertValue(cx, name, JSTYPE_STRING, &argv[0])) return JS_FALSE; name = argv[0]; namestr = JSVAL_TO_STRING(name); } xml = CHECK_COPY_ON_WRITE(cx, xml, obj); if (!xml) return JS_FALSE; xml->name->localName = namestr; return JS_TRUE; } static JSBool xml_setName(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) { JSXML *xml, *nsowner; jsval name; JSXMLQName *nameqn; JSObject *nameobj; JSXMLArray *nsarray; uint32 i, n; JSXMLNamespace *ns; NON_LIST_XML_METHOD_PROLOG; if (!JSXML_HAS_NAME(xml)) return JS_TRUE; name = argv[0]; if (!JSVAL_IS_PRIMITIVE(name) && OBJ_GET_CLASS(cx, JSVAL_TO_OBJECT(name)) == &js_QNameClass.base && !(nameqn = (JSXMLQName *) JS_GetPrivate(cx, JSVAL_TO_OBJECT(name))) ->uri) { name = argv[0] = STRING_TO_JSVAL(nameqn->localName); } nameobj = js_ConstructObject(cx, &js_QNameClass.base, NULL, NULL, 1, &name); if (!nameobj) return JS_FALSE; nameqn = (JSXMLQName *) JS_GetPrivate(cx, nameobj); /* ECMA-357 13.4.4.35 Step 4. */ if (xml->xml_class == JSXML_CLASS_PROCESSING_INSTRUCTION) nameqn->uri = cx->runtime->emptyString; xml = CHECK_COPY_ON_WRITE(cx, xml, obj); if (!xml) return JS_FALSE; xml->name = nameqn; /* * Erratum: nothing in 13.4.4.35 talks about making the name match the * in-scope namespaces, either by finding an in-scope namespace with a * matching uri and setting the new name's prefix to that namespace's * prefix, or by extending the in-scope namespaces for xml (which are in * xml->parent if xml is an attribute or a PI). */ if (xml->xml_class == JSXML_CLASS_ELEMENT) { nsowner = xml; } else { if (!xml->parent || xml->parent->xml_class != JSXML_CLASS_ELEMENT) return JS_TRUE; nsowner = xml->parent; } if (nameqn->prefix) { /* * The name being set has a prefix, which originally came from some * namespace object (which may be the null namespace, where both the * prefix and uri are the empty string). We must go through a full * GetNamespace in case that namespace is in-scope in nsowner. * * If we find such an in-scope namespace, we return true right away, * in this block. Otherwise, we fall through to the final return of * AddInScopeNamespace(cx, nsowner, ns). */ ns = GetNamespace(cx, nameqn, &nsowner->xml_namespaces); if (!ns) return JS_FALSE; /* XXXbe have to test membership to see whether GetNamespace added */ if (XMLARRAY_HAS_MEMBER(&nsowner->xml_namespaces, ns, NULL)) return JS_TRUE; } else { /* * At this point, we know nameqn->prefix is null, so nameqn->uri can't * be the empty string (the null namespace always uses the empty string * for both prefix and uri). * * This means we must inline GetNamespace and specialize it to match * uri only, never prefix. If we find a namespace with nameqn's uri * already in nsowner->xml_namespaces, then all that we need do is set * nameqn->prefix to that namespace's prefix. * * If no such namespace exists, we can create one without going through * the constructor, because we know nameqn->uri is non-empty (so prefix * does not need to be converted from null to empty by QName). */ JS_ASSERT(!IS_EMPTY(nameqn->uri)); nsarray = &nsowner->xml_namespaces; for (i = 0, n = nsarray->length; i < n; i++) { ns = XMLARRAY_MEMBER(nsarray, i, JSXMLNamespace); if (ns && js_EqualStrings(ns->uri, nameqn->uri)) { nameqn->prefix = ns->prefix; return JS_TRUE; } } ns = js_NewXMLNamespace(cx, NULL, nameqn->uri, JS_TRUE); if (!ns) return JS_FALSE; } return AddInScopeNamespace(cx, nsowner, ns); } static JSBool xml_setNamespace(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) { JSXML *xml, *nsowner; JSObject *nsobj, *qnobj; JSXMLNamespace *ns; jsval qnargv[2]; NON_LIST_XML_METHOD_PROLOG; if (!JSXML_HAS_NAME(xml)) return JS_TRUE; xml = CHECK_COPY_ON_WRITE(cx, xml, obj); if (!xml || !js_GetXMLQNameObject(cx, xml->name)) return JS_FALSE; nsobj = js_ConstructObject(cx, &js_NamespaceClass.base, NULL, obj, 1, argv); if (!nsobj) return JS_FALSE; ns = (JSXMLNamespace *) JS_GetPrivate(cx, nsobj); ns->declared = JS_TRUE; qnargv[0] = argv[0] = OBJECT_TO_JSVAL(nsobj); qnargv[1] = OBJECT_TO_JSVAL(xml->name->object); qnobj = js_ConstructObject(cx, &js_QNameClass.base, NULL, NULL, 2, qnargv); if (!qnobj) return JS_FALSE; xml->name = (JSXMLQName *) JS_GetPrivate(cx, qnobj); /* * Erratum: the spec fails to update the governing in-scope namespaces. * See the erratum noted in xml_setName, above. */ if (xml->xml_class == JSXML_CLASS_ELEMENT) { nsowner = xml; } else { if (!xml->parent || xml->parent->xml_class != JSXML_CLASS_ELEMENT) return JS_TRUE; nsowner = xml->parent; } return AddInScopeNamespace(cx, nsowner, ns); } /* XML and XMLList */ static JSBool xml_text(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) { JSXML *xml, *list, *kid, *vxml; uint32 i, n; JSBool ok; JSObject *kidobj; jsval v; XML_METHOD_PROLOG; list = xml_list_helper(cx, xml, rval); if (!list) return JS_FALSE; if (xml->xml_class == JSXML_CLASS_LIST) { ok = JS_TRUE; for (i = 0, n = xml->xml_kids.length; i < n; i++) { kid = XMLARRAY_MEMBER(&xml->xml_kids, i, JSXML); if (kid && kid->xml_class == JSXML_CLASS_ELEMENT) { ok = js_EnterLocalRootScope(cx); if (!ok) break; kidobj = js_GetXMLObject(cx, kid); if (kidobj) { ok = xml_text(cx, kidobj, argc, argv, &v); } else { ok = JS_FALSE; v = JSVAL_NULL; } js_LeaveLocalRootScopeWithResult(cx, v); if (!ok) return JS_FALSE; vxml = (JSXML *) JS_GetPrivate(cx, JSVAL_TO_OBJECT(v)); if (JSXML_LENGTH(vxml) != 0 && !Append(cx, list, vxml)) return JS_FALSE; } } } else { for (i = 0, n = JSXML_LENGTH(xml); i < n; i++) { kid = XMLARRAY_MEMBER(&xml->xml_kids, i, JSXML); if (kid && kid->xml_class == JSXML_CLASS_TEXT) { if (!Append(cx, list, kid)) return JS_FALSE; } } } return JS_TRUE; } /* XML and XMLList */ static JSBool xml_toXMLString(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) { JSString *str; str = ToXMLString(cx, OBJECT_TO_JSVAL(obj)); if (!str) return JS_FALSE; *rval = STRING_TO_JSVAL(str); return JS_TRUE; } /* XML and XMLList */ static JSString * xml_toString_helper(JSContext *cx, JSXML *xml) { JSString *str, *kidstr; JSXML *kid; JSXMLArrayCursor cursor; if (xml->xml_class == JSXML_CLASS_ATTRIBUTE || xml->xml_class == JSXML_CLASS_TEXT) { return xml->xml_value; } if (!HasSimpleContent(xml)) return ToXMLString(cx, OBJECT_TO_JSVAL(xml->object)); str = cx->runtime->emptyString; js_EnterLocalRootScope(cx); XMLArrayCursorInit(&cursor, &xml->xml_kids); while ((kid = (JSXML *) XMLArrayCursorNext(&cursor)) != NULL) { if (kid->xml_class != JSXML_CLASS_COMMENT && kid->xml_class != JSXML_CLASS_PROCESSING_INSTRUCTION) { kidstr = xml_toString_helper(cx, kid); if (!kidstr) { str = NULL; break; } str = js_ConcatStrings(cx, str, kidstr); if (!str) break; } } XMLArrayCursorFinish(&cursor); js_LeaveLocalRootScopeWithResult(cx, STRING_TO_JSVAL(str)); return str; } static JSBool xml_toString(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) { JSXML *xml; JSString *str; XML_METHOD_PROLOG; str = xml_toString_helper(cx, xml); if (!str) return JS_FALSE; *rval = STRING_TO_JSVAL(str); return JS_TRUE; } /* XML and XMLList */ static JSBool xml_valueOf(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) { *rval = OBJECT_TO_JSVAL(obj); return JS_TRUE; } static JSFunctionSpec xml_methods[] = { {"addNamespace", xml_addNamespace, 1,0,0}, {"appendChild", xml_appendChild, 1,0,0}, {js_attribute_str, xml_attribute, 1,0,0}, {"attributes", xml_attributes, 0,0,0}, {"child", xml_child, 1,0,0}, {"childIndex", xml_childIndex, 0,0,0}, {"children", xml_children, 0,0,0}, {"comments", xml_comments, 0,0,0}, {"contains", xml_contains, 1,0,0}, {"copy", xml_copy, 0,0,0}, {"descendants", xml_descendants, 1,0,0}, {"elements", xml_elements, 1,0,0}, {"hasOwnProperty", xml_hasOwnProperty, 1,0,0}, {"hasComplexContent", xml_hasComplexContent, 1,0,0}, {"hasSimpleContent", xml_hasSimpleContent, 1,0,0}, {"inScopeNamespaces", xml_inScopeNamespaces, 0,0,0}, {"insertChildAfter", xml_insertChildAfter, 2,0,0}, {"insertChildBefore", xml_insertChildBefore, 2,0,0}, {js_length_str, xml_length, 0,0,0}, {js_localName_str, xml_localName, 0,0,0}, {js_name_str, xml_name, 0,0,0}, {js_namespace_str, xml_namespace, 1,0,0}, {"namespaceDeclarations", xml_namespaceDeclarations, 0,0,0}, {"nodeKind", xml_nodeKind, 0,0,0}, {"normalize", xml_normalize, 0,0,0}, {js_xml_parent_str, xml_parent, 0,0,0}, {"processingInstructions",xml_processingInstructions,1,0,0}, {"prependChild", xml_prependChild, 1,0,0}, {"propertyIsEnumerable", xml_propertyIsEnumerable, 1,0,0}, {"removeNamespace", xml_removeNamespace, 1,0,0}, {"replace", xml_replace, 2,0,0}, {"setChildren", xml_setChildren, 1,0,0}, {"setLocalName", xml_setLocalName, 1,0,0}, {"setName", xml_setName, 1,0,0}, {"setNamespace", xml_setNamespace, 1,0,0}, {js_text_str, xml_text, 0,0,0}, {js_toString_str, xml_toString, 0,0,0}, {js_toXMLString_str, xml_toXMLString, 0,0,0}, {js_toSource_str, xml_toXMLString, 0,0,0}, {js_valueOf_str, xml_valueOf, 0,0,0}, {0,0,0,0,0} }; static JSBool CopyXMLSettings(JSContext *cx, JSObject *from, JSObject *to) { int i; const char *name; jsval v; for (i = XML_IGNORE_COMMENTS; i < XML_PRETTY_INDENT; i++) { name = xml_static_props[i].name; if (!JS_GetProperty(cx, from, name, &v)) return JS_FALSE; if (JSVAL_IS_BOOLEAN(v) && !JS_SetProperty(cx, to, name, &v)) return JS_FALSE; } name = xml_static_props[i].name; if (!JS_GetProperty(cx, from, name, &v)) return JS_FALSE; if (JSVAL_IS_NUMBER(v) && !JS_SetProperty(cx, to, name, &v)) return JS_FALSE; return JS_TRUE; } static JSBool SetDefaultXMLSettings(JSContext *cx, JSObject *obj) { int i; jsval v; for (i = XML_IGNORE_COMMENTS; i < XML_PRETTY_INDENT; i++) { v = JSVAL_TRUE; if (!JS_SetProperty(cx, obj, xml_static_props[i].name, &v)) return JS_FALSE; } v = INT_TO_JSVAL(2); return JS_SetProperty(cx, obj, xml_static_props[i].name, &v); } static JSBool xml_settings(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) { JSObject *settings; settings = JS_NewObject(cx, NULL, NULL, NULL); if (!settings) return JS_FALSE; *rval = OBJECT_TO_JSVAL(settings); return CopyXMLSettings(cx, obj, settings); } static JSBool xml_setSettings(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) { jsval v; JSBool ok; JSObject *settings; v = argv[0]; if (JSVAL_IS_NULL(v) || JSVAL_IS_VOID(v)) { cx->xmlSettingFlags = 0; ok = SetDefaultXMLSettings(cx, obj); } else { if (JSVAL_IS_PRIMITIVE(v)) return JS_TRUE; settings = JSVAL_TO_OBJECT(v); cx->xmlSettingFlags = 0; ok = CopyXMLSettings(cx, settings, obj); } if (ok) cx->xmlSettingFlags |= XSF_CACHE_VALID; return ok; } static JSBool xml_defaultSettings(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) { JSObject *settings; settings = JS_NewObject(cx, NULL, NULL, NULL); if (!settings) return JS_FALSE; *rval = OBJECT_TO_JSVAL(settings); return SetDefaultXMLSettings(cx, settings); } static JSFunctionSpec xml_static_methods[] = { {"settings", xml_settings, 0,0,0}, {"setSettings", xml_setSettings, 1,0,0}, {"defaultSettings", xml_defaultSettings, 0,0,0}, {0,0,0,0,0} }; static JSBool XML(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) { jsval v; JSXML *xml, *copy; JSObject *xobj, *vobj; JSClass *clasp; v = argv[0]; if (JSVAL_IS_NULL(v) || JSVAL_IS_VOID(v)) v = STRING_TO_JSVAL(cx->runtime->emptyString); xobj = ToXML(cx, v); if (!xobj) return JS_FALSE; *rval = OBJECT_TO_JSVAL(xobj); xml = (JSXML *) JS_GetPrivate(cx, xobj); if ((cx->fp->flags & JSFRAME_CONSTRUCTING) && !JSVAL_IS_PRIMITIVE(v)) { vobj = JSVAL_TO_OBJECT(v); clasp = OBJ_GET_CLASS(cx, vobj); if (clasp == &js_XMLClass || (clasp->flags & JSCLASS_DOCUMENT_OBSERVER)) { /* No need to lock obj, it's newly constructed and thread local. */ copy = DeepCopy(cx, xml, obj, 0); if (!copy) return JS_FALSE; JS_ASSERT(copy->object == obj); *rval = OBJECT_TO_JSVAL(obj); return JS_TRUE; } } return JS_TRUE; } static JSBool XMLList(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) { jsval v; JSObject *vobj, *listobj; JSXML *xml, *list; v = argv[0]; if (JSVAL_IS_NULL(v) || JSVAL_IS_VOID(v)) v = STRING_TO_JSVAL(cx->runtime->emptyString); if ((cx->fp->flags & JSFRAME_CONSTRUCTING) && !JSVAL_IS_PRIMITIVE(v)) { vobj = JSVAL_TO_OBJECT(v); if (OBJECT_IS_XML(cx, vobj)) { xml = (JSXML *) JS_GetPrivate(cx, vobj); if (xml->xml_class == JSXML_CLASS_LIST) { listobj = js_NewXMLObject(cx, JSXML_CLASS_LIST); if (!listobj) return JS_FALSE; *rval = OBJECT_TO_JSVAL(listobj); list = (JSXML *) JS_GetPrivate(cx, listobj); if (!Append(cx, list, xml)) return JS_FALSE; return JS_TRUE; } } } /* Toggle on XML support since the script has explicitly requested it. */ listobj = ToXMLList(cx, v); if (!listobj) return JS_FALSE; *rval = OBJECT_TO_JSVAL(listobj); return JS_TRUE; } #define JSXML_LIST_SIZE (offsetof(JSXML, u) + sizeof(struct JSXMLListVar)) #define JSXML_ELEMENT_SIZE (offsetof(JSXML, u) + sizeof(struct JSXMLVar)) #define JSXML_LEAF_SIZE (offsetof(JSXML, u) + sizeof(JSString *)) static size_t sizeof_JSXML[JSXML_CLASS_LIMIT] = { JSXML_LIST_SIZE, /* JSXML_CLASS_LIST */ JSXML_ELEMENT_SIZE, /* JSXML_CLASS_ELEMENT */ JSXML_LEAF_SIZE, /* JSXML_CLASS_ATTRIBUTE */ JSXML_LEAF_SIZE, /* JSXML_CLASS_PROCESSING_INSTRUCTION */ JSXML_LEAF_SIZE, /* JSXML_CLASS_TEXT */ JSXML_LEAF_SIZE /* JSXML_CLASS_COMMENT */ }; #ifdef DEBUG_notme JSCList xml_leaks = JS_INIT_STATIC_CLIST(&xml_leaks); uint32 xml_serial; #endif JSXML * js_NewXML(JSContext *cx, JSXMLClass xml_class) { JSXML *xml; xml = (JSXML *) js_NewGCThing(cx, GCX_XML, sizeof_JSXML[xml_class]); if (!xml) return NULL; xml->object = NULL; xml->domnode = NULL; xml->parent = NULL; xml->name = NULL; xml->xml_class = xml_class; xml->xml_flags = 0; if (JSXML_CLASS_HAS_VALUE(xml_class)) { xml->xml_value = cx->runtime->emptyString; } else { XMLArrayInit(cx, &xml->xml_kids, 0); if (xml_class == JSXML_CLASS_LIST) { xml->xml_target = NULL; xml->xml_targetprop = NULL; } else { XMLArrayInit(cx, &xml->xml_namespaces, 0); XMLArrayInit(cx, &xml->xml_attrs, 0); } } #ifdef DEBUG_notme JS_APPEND_LINK(&xml->links, &xml_leaks); xml->serial = xml_serial++; #endif METER(xml_stats.xml); METER(xml_stats.livexml); return xml; } void js_MarkXML(JSContext *cx, JSXML *xml) { GC_MARK(cx, xml->object, "object"); GC_MARK(cx, xml->name, "name"); GC_MARK(cx, xml->parent, "xml_parent"); if (JSXML_HAS_VALUE(xml)) { GC_MARK(cx, xml->xml_value, "value"); return; } xml_mark_vector(cx, (JSXML **) xml->xml_kids.vector, xml->xml_kids.length); XMLArrayCursorMark(cx, xml->xml_kids.cursors); XMLArrayTrim(&xml->xml_kids); if (xml->xml_class == JSXML_CLASS_LIST) { if (xml->xml_target) GC_MARK(cx, xml->xml_target, "target"); if (xml->xml_targetprop) GC_MARK(cx, xml->xml_targetprop, "targetprop"); } else { namespace_mark_vector(cx, (JSXMLNamespace **) xml->xml_namespaces.vector, xml->xml_namespaces.length); XMLArrayCursorMark(cx, xml->xml_namespaces.cursors); XMLArrayTrim(&xml->xml_namespaces); xml_mark_vector(cx, (JSXML **) xml->xml_attrs.vector, xml->xml_attrs.length); XMLArrayCursorMark(cx, xml->xml_attrs.cursors); XMLArrayTrim(&xml->xml_attrs); } } void js_FinalizeXML(JSContext *cx, JSXML *xml) { if (JSXML_HAS_KIDS(xml)) { XMLArrayFinish(cx, &xml->xml_kids); if (xml->xml_class == JSXML_CLASS_ELEMENT) { XMLArrayFinish(cx, &xml->xml_namespaces); XMLArrayFinish(cx, &xml->xml_attrs); } } #ifdef DEBUG_notme JS_REMOVE_LINK(&xml->links); #endif UNMETER(xml_stats.livexml); } JSObject * js_ParseNodeToXMLObject(JSContext *cx, JSParseNode *pn) { jsval nsval; JSXMLNamespace *ns; JSXMLArray nsarray; JSXML *xml; if (!js_GetDefaultXMLNamespace(cx, &nsval)) return NULL; JS_ASSERT(!JSVAL_IS_PRIMITIVE(nsval)); ns = (JSXMLNamespace *) JS_GetPrivate(cx, JSVAL_TO_OBJECT(nsval)); if (!XMLArrayInit(cx, &nsarray, 1)) return NULL; XMLARRAY_APPEND(cx, &nsarray, ns); xml = ParseNodeToXML(cx, pn, &nsarray, XSF_PRECOMPILED_ROOT); XMLArrayFinish(cx, &nsarray); if (!xml) return NULL; return xml->object; } JSObject * js_NewXMLObject(JSContext *cx, JSXMLClass xml_class) { JSXML *xml; JSObject *obj; JSTempValueRooter tvr; xml = js_NewXML(cx, xml_class); if (!xml) return NULL; JS_PUSH_TEMP_ROOT_GCTHING(cx, xml, &tvr); obj = js_GetXMLObject(cx, xml); JS_POP_TEMP_ROOT(cx, &tvr); return obj; } static JSObject * NewXMLObject(JSContext *cx, JSXML *xml) { JSObject *obj; obj = js_NewObject(cx, &js_XMLClass, NULL, NULL); if (!obj || !JS_SetPrivate(cx, obj, xml)) { cx->weakRoots.newborn[GCX_OBJECT] = NULL; return NULL; } METER(xml_stats.xmlobj); METER(xml_stats.livexmlobj); return obj; } JSObject * js_GetXMLObject(JSContext *cx, JSXML *xml) { JSObject *obj; obj = xml->object; if (obj) { JS_ASSERT(JS_GetPrivate(cx, obj) == xml); return obj; } /* * A JSXML cannot be shared among threads unless it has an object. * A JSXML cannot be given an object unless: * (a) it has no parent; or * (b) its parent has no object (therefore is thread-private); or * (c) its parent's object is locked. * * Once given an object, a JSXML is immutable. */ JS_ASSERT(!xml->parent || !xml->parent->object || JS_IS_OBJ_LOCKED(cx, xml->parent->object)); obj = NewXMLObject(cx, xml); if (!obj) return NULL; xml->object = obj; return obj; } JSObject * js_InitNamespaceClass(JSContext *cx, JSObject *obj) { return JS_InitClass(cx, obj, NULL, &js_NamespaceClass.base, Namespace, 2, namespace_props, namespace_methods, NULL, NULL); } JSObject * js_InitQNameClass(JSContext *cx, JSObject *obj) { return JS_InitClass(cx, obj, NULL, &js_QNameClass.base, QName, 2, qname_props, qname_methods, NULL, NULL); } JSObject * js_InitAttributeNameClass(JSContext *cx, JSObject *obj) { return JS_InitClass(cx, obj, NULL, &js_AttributeNameClass, AttributeName, 2, qname_props, qname_methods, NULL, NULL); } JSObject * js_InitAnyNameClass(JSContext *cx, JSObject *obj) { jsval v; if (!js_GetAnyName(cx, &v)) return NULL; return JSVAL_TO_OBJECT(v); } JSObject * js_InitXMLClass(JSContext *cx, JSObject *obj) { JSObject *proto, *pobj, *ctor; JSFunction *fun; JSXML *xml; JSProperty *prop; JSScopeProperty *sprop; jsval cval, argv[1], junk; /* Define the isXMLName function. */ if (!JS_DefineFunction(cx, obj, js_isXMLName_str, xml_isXMLName, 1, 0)) return NULL; /* Define the XML class constructor and prototype. */ proto = JS_InitClass(cx, obj, NULL, &js_XMLClass, XML, 1, NULL, xml_methods, xml_static_props, xml_static_methods); if (!proto) return NULL; xml = js_NewXML(cx, JSXML_CLASS_TEXT); if (!xml || !JS_SetPrivate(cx, proto, xml)) return NULL; xml->object = proto; METER(xml_stats.xmlobj); METER(xml_stats.livexmlobj); /* * Prepare to set default settings on the XML constructor we just made. * NB: We can't use JS_GetConstructor, because it calls OBJ_GET_PROPERTY, * which is xml_getProperty, which creates a new XMLList every time! We * must instead call js_LookupProperty directly. */ if (!js_LookupProperty(cx, proto, ATOM_TO_JSID(cx->runtime->atomState.constructorAtom), &pobj, &prop)) { return NULL; } JS_ASSERT(prop); sprop = (JSScopeProperty *) prop; JS_ASSERT(SPROP_HAS_VALID_SLOT(sprop, OBJ_SCOPE(pobj))); cval = OBJ_GET_SLOT(cx, pobj, sprop->slot); OBJ_DROP_PROPERTY(cx, pobj, prop); JS_ASSERT(VALUE_IS_FUNCTION(cx, cval)); /* Set default settings. */ ctor = JSVAL_TO_OBJECT(cval); argv[0] = JSVAL_VOID; if (!xml_setSettings(cx, ctor, 1, argv, &junk)) return NULL; /* Define the XMLList function and give it the same prototype as XML. */ fun = JS_DefineFunction(cx, obj, js_XMLList_str, XMLList, 1, 0); if (!fun) return NULL; if (!js_SetClassPrototype(cx, fun->object, proto, JSPROP_READONLY | JSPROP_PERMANENT)) { return NULL; } return proto; } JSObject * js_InitXMLClasses(JSContext *cx, JSObject *obj) { if (!js_InitNamespaceClass(cx, obj)) return NULL; if (!js_InitQNameClass(cx, obj)) return NULL; if (!js_InitAttributeNameClass(cx, obj)) return NULL; if (!js_InitAnyNameClass(cx, obj)) return NULL; return js_InitXMLClass(cx, obj); } JSBool js_GetFunctionNamespace(JSContext *cx, jsval *vp) { JSRuntime *rt; JSObject *obj; JSAtom *atom; JSString *prefix, *uri; /* An invalid URI, for internal use only, guaranteed not to collide. */ static const char anti_uri[] = "@mozilla.org/js/function"; /* Optimize by avoiding JS_LOCK_GC(rt) for the common case. */ rt = cx->runtime; obj = rt->functionNamespaceObject; if (!obj) { JS_LOCK_GC(rt); obj = rt->functionNamespaceObject; if (!obj) { JS_UNLOCK_GC(rt); atom = js_Atomize(cx, js_function_str, 8, 0); JS_ASSERT(atom); prefix = ATOM_TO_STRING(atom); /* * Note that any race to atomize anti_uri here is resolved by * the atom table code, such that at most one atom for anti_uri * is created. We store in rt->atomState.lazy unconditionally, * since we are guaranteed to overwrite either null or the same * atom pointer. */ atom = js_Atomize(cx, anti_uri, sizeof anti_uri - 1, ATOM_PINNED); if (!atom) return JS_FALSE; rt->atomState.lazy.functionNamespaceURIAtom = atom; uri = ATOM_TO_STRING(atom); obj = js_NewXMLNamespaceObject(cx, prefix, uri, JS_FALSE); if (!obj) return JS_FALSE; /* * Avoid entraining any in-scope Object.prototype. The loss of * Namespace.prototype is not detectable, as there is no way to * refer to this instance in scripts. When used to qualify method * names, its prefix and uri references are copied to the QName. */ OBJ_SET_PROTO(cx, obj, NULL); OBJ_SET_PARENT(cx, obj, NULL); JS_LOCK_GC(rt); if (!rt->functionNamespaceObject) rt->functionNamespaceObject = obj; else obj = rt->functionNamespaceObject; } JS_UNLOCK_GC(rt); } *vp = OBJECT_TO_JSVAL(obj); return JS_TRUE; } /* * Note the asymmetry between js_GetDefaultXMLNamespace and js_SetDefaultXML- * Namespace. Get searches fp->scopeChain for JS_DEFAULT_XML_NAMESPACE_ID, * while Set sets JS_DEFAULT_XML_NAMESPACE_ID in fp->varobj (unless fp is a * lightweight function activation). There's no requirement that fp->varobj * lie directly on fp->scopeChain, although it should be reachable using the * prototype chain from a scope object (cf. JSOPTION_VAROBJFIX in jsapi.h). * * If Get can't find JS_DEFAULT_XML_NAMESPACE_ID along the scope chain, it * creates a default namespace via 'new Namespace()'. In contrast, Set uses * its v argument as the uri of a new Namespace, with "" as the prefix. See * ECMA-357 12.1 and 12.1.1. Note that if Set is called with a Namespace n, * the default XML namespace will be set to ("", n.uri). So the uri string * is really the only usefully stored value of the default namespace. */ JSBool js_GetDefaultXMLNamespace(JSContext *cx, jsval *vp) { JSStackFrame *fp; JSObject *nsobj, *obj, *tmp; jsval v; fp = cx->fp; nsobj = fp->xmlNamespace; if (nsobj) { *vp = OBJECT_TO_JSVAL(nsobj); return JS_TRUE; } obj = NULL; for (tmp = fp->scopeChain; tmp; tmp = OBJ_GET_PARENT(cx, obj)) { obj = tmp; if (!OBJ_GET_PROPERTY(cx, obj, JS_DEFAULT_XML_NAMESPACE_ID, &v)) return JS_FALSE; if (!JSVAL_IS_PRIMITIVE(v)) { fp->xmlNamespace = JSVAL_TO_OBJECT(v); *vp = v; return JS_TRUE; } } nsobj = js_ConstructObject(cx, &js_NamespaceClass.base, NULL, obj, 0, NULL); if (!nsobj) return JS_FALSE; v = OBJECT_TO_JSVAL(nsobj); if (obj && !OBJ_DEFINE_PROPERTY(cx, obj, JS_DEFAULT_XML_NAMESPACE_ID, v, JS_PropertyStub, JS_PropertyStub, JSPROP_PERMANENT, NULL)) { return JS_FALSE; } fp->xmlNamespace = nsobj; *vp = v; return JS_TRUE; } JSBool js_SetDefaultXMLNamespace(JSContext *cx, jsval v) { jsval argv[2]; JSObject *nsobj, *varobj; JSStackFrame *fp; argv[0] = STRING_TO_JSVAL(cx->runtime->emptyString); argv[1] = v; nsobj = js_ConstructObject(cx, &js_NamespaceClass.base, NULL, NULL, 2, argv); if (!nsobj) return JS_FALSE; v = OBJECT_TO_JSVAL(nsobj); fp = cx->fp; varobj = fp->varobj; if (varobj) { if (!OBJ_DEFINE_PROPERTY(cx, varobj, JS_DEFAULT_XML_NAMESPACE_ID, v, JS_PropertyStub, JS_PropertyStub, JSPROP_PERMANENT, NULL)) { return JS_FALSE; } } else { JS_ASSERT(fp->fun && !JSFUN_HEAVYWEIGHT_TEST(fp->fun->flags)); } fp->xmlNamespace = JSVAL_TO_OBJECT(v); return JS_TRUE; } JSBool js_ToAttributeName(JSContext *cx, jsval *vp) { JSXMLQName *qn; qn = ToAttributeName(cx, *vp); if (!qn) return JS_FALSE; *vp = OBJECT_TO_JSVAL(qn->object); return JS_TRUE; } JSString * js_EscapeAttributeValue(JSContext *cx, JSString *str) { return EscapeAttributeValue(cx, NULL, str); } JSString * js_AddAttributePart(JSContext *cx, JSBool isName, JSString *str, JSString *str2) { size_t len, len2, newlen; jschar *chars; if (JSSTRING_IS_DEPENDENT(str) || !(*js_GetGCThingFlags(str) & GCF_MUTABLE)) { str = js_NewStringCopyN(cx, JSSTRING_CHARS(str), JSSTRING_LENGTH(str), 0); if (!str) return NULL; } len = str->length; len2 = JSSTRING_LENGTH(str2); newlen = (isName) ? len + 1 + len2 : len + 2 + len2 + 1; chars = (jschar *) JS_realloc(cx, str->chars, (newlen+1) * sizeof(jschar)); if (!chars) return NULL; /* * Reallocating str (because we know it has no other references) requires * purging any deflated string cached for it. */ js_PurgeDeflatedStringCache(cx->runtime, str); str->chars = chars; str->length = newlen; chars += len; if (isName) { *chars++ = ' '; js_strncpy(chars, JSSTRING_CHARS(str2), len2); chars += len2; } else { *chars++ = '='; *chars++ = '"'; js_strncpy(chars, JSSTRING_CHARS(str2), len2); chars += len2; *chars++ = '"'; } *chars = 0; return str; } JSString * js_EscapeElementValue(JSContext *cx, JSString *str) { return EscapeElementValue(cx, NULL, str); } JSString * js_ValueToXMLString(JSContext *cx, jsval v) { return ToXMLString(cx, v); } static JSBool anyname_toString(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) { *rval = ATOM_KEY(cx->runtime->atomState.starAtom); return JS_TRUE; } JSBool js_GetAnyName(JSContext *cx, jsval *vp) { JSRuntime *rt; JSObject *obj; JSXMLQName *qn; JSBool ok; /* Optimize by avoiding JS_LOCK_GC(rt) for the common case. */ rt = cx->runtime; obj = rt->anynameObject; if (!obj) { JS_LOCK_GC(rt); obj = rt->anynameObject; if (!obj) { JS_UNLOCK_GC(rt); /* * Protect multiple newborns created below, in the do-while(0) * loop used to ensure that we leave this local root scope. */ ok = js_EnterLocalRootScope(cx); if (!ok) return JS_FALSE; do { qn = js_NewXMLQName(cx, rt->emptyString, rt->emptyString, ATOM_TO_STRING(rt->atomState.starAtom)); if (!qn) { ok = JS_FALSE; break; } obj = js_NewObject(cx, &js_AnyNameClass, NULL, NULL); if (!obj || !JS_SetPrivate(cx, obj, qn)) { cx->weakRoots.newborn[GCX_OBJECT] = NULL; ok = JS_FALSE; break; } qn->object = obj; METER(xml_stats.qnameobj); METER(xml_stats.liveqnameobj); /* * Avoid entraining any Object.prototype found via cx's scope * chain or global object. This loses the default toString, * but no big deal: we want to customize toString anyway for * clearer diagnostics. */ if (!JS_DefineFunction(cx, obj, js_toString_str, anyname_toString, 0, 0)) { ok = JS_FALSE; break; } OBJ_SET_PROTO(cx, obj, NULL); JS_ASSERT(!OBJ_GET_PARENT(cx, obj)); } while (0); js_LeaveLocalRootScopeWithResult(cx, OBJECT_TO_JSVAL(obj)); if (!ok) return JS_FALSE; JS_LOCK_GC(rt); if (!rt->anynameObject) rt->anynameObject = obj; else obj = rt->anynameObject; } JS_UNLOCK_GC(rt); } *vp = OBJECT_TO_JSVAL(obj); return JS_TRUE; } JSBool js_FindXMLProperty(JSContext *cx, jsval name, JSObject **objp, jsval *namep) { JSXMLQName *qn; jsid funid, id; JSObject *obj, *pobj, *lastobj; JSProperty *prop; const char *printable; qn = ToXMLName(cx, name, &funid); if (!qn) return JS_FALSE; id = OBJECT_TO_JSID(qn->object); obj = cx->fp->scopeChain; do { if (!OBJ_LOOKUP_PROPERTY(cx, obj, id, &pobj, &prop)) return JS_FALSE; if (prop) { OBJ_DROP_PROPERTY(cx, pobj, prop); /* * Call OBJ_THIS_OBJECT to skip any With object that wraps an XML * object to carry scope chain linkage in js_FilterXMLList. */ pobj = OBJ_THIS_OBJECT(cx, obj); if (OBJECT_IS_XML(cx, pobj)) { *objp = pobj; *namep = ID_TO_VALUE(id); return JS_TRUE; } } lastobj = obj; } while ((obj = OBJ_GET_PARENT(cx, obj)) != NULL); printable = js_ValueToPrintableString(cx, name); if (printable) { JS_ReportErrorFlagsAndNumber(cx, JSREPORT_ERROR, js_GetErrorMessage, NULL, JSMSG_UNDEFINED_XML_NAME, printable); } return JS_FALSE; } JSBool js_GetXMLProperty(JSContext *cx, JSObject *obj, jsval name, jsval *vp) { return GetProperty(cx, obj, name, vp); } JSBool js_GetXMLFunction(JSContext *cx, JSObject *obj, jsid id, jsval *vp) { JSObject *target; JSXML *xml; JSTempValueRooter tvr; JSBool ok; JS_ASSERT(OBJECT_IS_XML(cx, obj)); /* After this point, control must flow through label out: to exit. */ JS_PUSH_TEMP_ROOT_OBJECT(cx, NULL, &tvr); /* * See comments before xml_lookupProperty about the need for the proto * chain lookup. */ target = obj; for (;;) { ok = js_GetProperty(cx, target, id, vp); if (!ok) goto out; if (VALUE_IS_FUNCTION(cx, *vp)) { ok = JS_TRUE; goto out; } target = OBJ_GET_PROTO(cx, target); if (target == NULL) break; tvr.u.object = target; } xml = (JSXML *) JS_GetPrivate(cx, obj); if (HasSimpleContent(xml)) { /* Search in String.prototype to implement 11.2.2.1 Step 3(f). */ ok = js_GetClassPrototype(cx, NULL, INT_TO_JSID(JSProto_String), &tvr.u.object); if (!ok) goto out; JS_ASSERT(tvr.u.object); ok = OBJ_GET_PROPERTY(cx, tvr.u.object, id, vp); } out: JS_POP_TEMP_ROOT(cx, &tvr); return ok; } JSBool js_SetXMLProperty(JSContext *cx, JSObject *obj, jsval name, jsval *vp) { return PutProperty(cx, obj, name, vp); } static JSXML * GetPrivate(JSContext *cx, JSObject *obj, const char *method) { JSXML *xml; xml = (JSXML *) JS_GetInstancePrivate(cx, obj, &js_XMLClass, NULL); if (!xml) { JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_INCOMPATIBLE_METHOD, js_XML_str, method, OBJ_GET_CLASS(cx, obj)->name); } return xml; } JSBool js_GetXMLDescendants(JSContext *cx, JSObject *obj, jsval id, jsval *vp) { JSXML *xml, *list; xml = GetPrivate(cx, obj, "descendants internal method"); if (!xml) return JS_FALSE; list = Descendants(cx, xml, id); if (!list) return JS_FALSE; *vp = OBJECT_TO_JSVAL(list->object); return JS_TRUE; } JSBool js_DeleteXMLListElements(JSContext *cx, JSObject *listobj) { JSXML *list; uint32 n; jsval junk; list = (JSXML *) JS_GetPrivate(cx, listobj); for (n = list->xml_kids.length; n != 0; --n) { if (!DeleteProperty(cx, listobj, INT_TO_JSID(0), &junk)) return JS_FALSE; } return JS_TRUE; } JSBool js_FilterXMLList(JSContext *cx, JSObject *obj, jsbytecode *pc, jsval *vp) { JSBool ok, match; JSStackFrame *fp; uint32 flags; JSObject *scobj, *listobj, *resobj, *withobj, *kidobj; JSXML *xml, *list, *result, *kid; JSXMLArrayCursor cursor; ok = js_EnterLocalRootScope(cx); if (!ok) return JS_FALSE; /* All control flow after this point must exit via label out or bad. */ *vp = JSVAL_NULL; fp = cx->fp; flags = fp->flags; fp->flags = flags | JSFRAME_FILTERING; scobj = js_GetScopeChain(cx, fp); withobj = NULL; if (!scobj) goto bad; xml = GetPrivate(cx, obj, "filtering predicate operator"); if (!xml) goto bad; if (xml->xml_class == JSXML_CLASS_LIST) { list = xml; } else { listobj = js_NewXMLObject(cx, JSXML_CLASS_LIST); if (!listobj) goto bad; list = (JSXML *) JS_GetPrivate(cx, listobj); ok = Append(cx, list, xml); if (!ok) goto out; } resobj = js_NewXMLObject(cx, JSXML_CLASS_LIST); if (!resobj) goto bad; result = (JSXML *) JS_GetPrivate(cx, resobj); /* Hoist the scope chain update out of the loop over kids. */ withobj = js_NewWithObject(cx, NULL, scobj, -1); if (!withobj) goto bad; fp->scopeChain = withobj; XMLArrayCursorInit(&cursor, &list->xml_kids); while ((kid = (JSXML *) XMLArrayCursorNext(&cursor)) != NULL) { kidobj = js_GetXMLObject(cx, kid); if (!kidobj) break; OBJ_SET_PROTO(cx, withobj, kidobj); ok = js_Interpret(cx, pc, vp) && js_ValueToBoolean(cx, *vp, &match); if (ok && match) ok = Append(cx, result, kid); if (!ok) break; } XMLArrayCursorFinish(&cursor); if (!ok) goto out; if (kid) goto bad; *vp = OBJECT_TO_JSVAL(resobj); out: fp->flags = flags | (fp->flags & JSFRAME_POP_BLOCKS); if (withobj) { fp->scopeChain = scobj; JS_SetPrivate(cx, withobj, NULL); } js_LeaveLocalRootScopeWithResult(cx, *vp); return ok; bad: ok = JS_FALSE; goto out; } JSObject * js_ValueToXMLObject(JSContext *cx, jsval v) { return ToXML(cx, v); } JSObject * js_ValueToXMLListObject(JSContext *cx, jsval v) { return ToXMLList(cx, v); } JSObject * js_CloneXMLObject(JSContext *cx, JSObject *obj) { uintN flags; JSXML *xml; if (!GetXMLSettingFlags(cx, &flags)) return NULL; xml = (JSXML *) JS_GetPrivate(cx, obj); if (flags & (XSF_IGNORE_COMMENTS | XSF_IGNORE_PROCESSING_INSTRUCTIONS | XSF_IGNORE_WHITESPACE)) { xml = DeepCopy(cx, xml, NULL, flags); if (!xml) return NULL; return xml->object; } return NewXMLObject(cx, xml); } JSObject * js_NewXMLSpecialObject(JSContext *cx, JSXMLClass xml_class, JSString *name, JSString *value) { uintN flags; JSObject *obj; JSXML *xml; JSXMLQName *qn; if (!GetXMLSettingFlags(cx, &flags)) return NULL; if ((xml_class == JSXML_CLASS_COMMENT && (flags & XSF_IGNORE_COMMENTS)) || (xml_class == JSXML_CLASS_PROCESSING_INSTRUCTION && (flags & XSF_IGNORE_PROCESSING_INSTRUCTIONS))) { return js_NewXMLObject(cx, JSXML_CLASS_TEXT); } obj = js_NewXMLObject(cx, xml_class); if (!obj) return NULL; xml = (JSXML *) JS_GetPrivate(cx, obj); if (name) { qn = js_NewXMLQName(cx, cx->runtime->emptyString, NULL, name); if (!qn) return NULL; xml->name = qn; } xml->xml_value = value; return obj; } JSString * js_MakeXMLCDATAString(JSContext *cx, JSString *str) { return MakeXMLCDATAString(cx, NULL, str); } JSString * js_MakeXMLCommentString(JSContext *cx, JSString *str) { return MakeXMLCommentString(cx, NULL, str); } JSString * js_MakeXMLPIString(JSContext *cx, JSString *name, JSString *str) { return MakeXMLPIString(cx, NULL, name, str); } #endif /* JS_HAS_XML_SUPPORT */ pacparser-1.4.5/src/spidermonkey/js/src/jsxml.h000066400000000000000000000241231464010763600215230ustar00rootroot00000000000000/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- * * ***** BEGIN LICENSE BLOCK ***** * Version: MPL 1.1/GPL 2.0/LGPL 2.1 * * The contents of this file are subject to the Mozilla Public License Version * 1.1 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * http://www.mozilla.org/MPL/ * * Software distributed under the License is distributed on an "AS IS" basis, * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License * for the specific language governing rights and limitations under the * License. * * The Original Code is SpiderMonkey E4X code, released August, 2004. * * The Initial Developer of the Original Code is * Netscape Communications Corporation. * Portions created by the Initial Developer are Copyright (C) 1998 * the Initial Developer. All Rights Reserved. * * Contributor(s): * * Alternatively, the contents of this file may be used under the terms of * either of the GNU General Public License Version 2 or later (the "GPL"), * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), * in which case the provisions of the GPL or the LGPL are applicable instead * of those above. If you wish to allow use of your version of this file only * under the terms of either the GPL or the LGPL, and not to allow others to * use your version of this file under the terms of the MPL, indicate your * decision by deleting the provisions above and replace them with the notice * and other provisions required by the GPL or the LGPL. If you do not delete * the provisions above, a recipient may use your version of this file under * the terms of any one of the MPL, the GPL or the LGPL. * * ***** END LICENSE BLOCK ***** */ #ifndef jsxml_h___ #define jsxml_h___ #include "jsstddef.h" #include "jspubtd.h" extern const char js_AnyName_str[]; extern const char js_AttributeName_str[]; extern const char js_isXMLName_str[]; extern const char js_XMLList_str[]; extern const char js_amp_entity_str[]; extern const char js_gt_entity_str[]; extern const char js_lt_entity_str[]; extern const char js_quot_entity_str[]; struct JSXMLNamespace { JSObject *object; JSString *prefix; JSString *uri; JSBool declared; /* true if declared in its XML tag */ }; extern JSXMLNamespace * js_NewXMLNamespace(JSContext *cx, JSString *prefix, JSString *uri, JSBool declared); extern void js_MarkXMLNamespace(JSContext *cx, JSXMLNamespace *ns); extern void js_FinalizeXMLNamespace(JSContext *cx, JSXMLNamespace *ns); extern JSObject * js_NewXMLNamespaceObject(JSContext *cx, JSString *prefix, JSString *uri, JSBool declared); extern JSObject * js_GetXMLNamespaceObject(JSContext *cx, JSXMLNamespace *ns); struct JSXMLQName { JSObject *object; JSString *uri; JSString *prefix; JSString *localName; }; extern JSXMLQName * js_NewXMLQName(JSContext *cx, JSString *uri, JSString *prefix, JSString *localName); extern void js_MarkXMLQName(JSContext *cx, JSXMLQName *qn); extern void js_FinalizeXMLQName(JSContext *cx, JSXMLQName *qn); extern JSObject * js_NewXMLQNameObject(JSContext *cx, JSString *uri, JSString *prefix, JSString *localName); extern JSObject * js_GetXMLQNameObject(JSContext *cx, JSXMLQName *qn); extern JSObject * js_GetAttributeNameObject(JSContext *cx, JSXMLQName *qn); extern JSObject * js_ConstructXMLQNameObject(JSContext *cx, jsval nsval, jsval lnval); typedef JSBool (* JS_DLL_CALLBACK JSIdentityOp)(const void *a, const void *b); struct JSXMLArray { uint32 length; uint32 capacity; void **vector; JSXMLArrayCursor *cursors; }; #define JSXML_PRESET_CAPACITY JS_BIT(31) #define JSXML_CAPACITY_MASK JS_BITMASK(31) #define JSXML_CAPACITY(array) ((array)->capacity & JSXML_CAPACITY_MASK) struct JSXMLArrayCursor { JSXMLArray *array; uint32 index; JSXMLArrayCursor *next; JSXMLArrayCursor **prevp; void *root; }; /* * NB: don't reorder this enum without changing all array initializers that * depend on it in jsxml.c. */ typedef enum JSXMLClass { JSXML_CLASS_LIST, JSXML_CLASS_ELEMENT, JSXML_CLASS_ATTRIBUTE, JSXML_CLASS_PROCESSING_INSTRUCTION, JSXML_CLASS_TEXT, JSXML_CLASS_COMMENT, JSXML_CLASS_LIMIT } JSXMLClass; #define JSXML_CLASS_HAS_KIDS(class_) ((class_) < JSXML_CLASS_ATTRIBUTE) #define JSXML_CLASS_HAS_VALUE(class_) ((class_) >= JSXML_CLASS_ATTRIBUTE) #define JSXML_CLASS_HAS_NAME(class_) \ ((uintN)((class_) - JSXML_CLASS_ELEMENT) <= \ (uintN)(JSXML_CLASS_PROCESSING_INSTRUCTION - JSXML_CLASS_ELEMENT)) #ifdef DEBUG_notme #include "jsclist.h" #endif struct JSXML { #ifdef DEBUG_notme JSCList links; uint32 serial; #endif JSObject *object; void *domnode; /* DOM node if mapped info item */ JSXML *parent; JSXMLQName *name; uint16 xml_class; /* discriminates u, below */ uint16 xml_flags; /* flags, see below */ union { struct JSXMLListVar { JSXMLArray kids; /* NB: must come first */ JSXML *target; JSXMLQName *targetprop; } list; struct JSXMLVar { JSXMLArray kids; /* NB: must come first */ JSXMLArray namespaces; JSXMLArray attrs; } elem; JSString *value; } u; /* Don't add anything after u -- see js_NewXML for why. */ }; /* union member shorthands */ #define xml_kids u.list.kids #define xml_target u.list.target #define xml_targetprop u.list.targetprop #define xml_namespaces u.elem.namespaces #define xml_attrs u.elem.attrs #define xml_value u.value /* xml_flags values */ #define XMLF_WHITESPACE_TEXT 0x1 /* xml_class-testing macros */ #define JSXML_HAS_KIDS(xml) JSXML_CLASS_HAS_KIDS((xml)->xml_class) #define JSXML_HAS_VALUE(xml) JSXML_CLASS_HAS_VALUE((xml)->xml_class) #define JSXML_HAS_NAME(xml) JSXML_CLASS_HAS_NAME((xml)->xml_class) #define JSXML_LENGTH(xml) (JSXML_CLASS_HAS_KIDS((xml)->xml_class) \ ? (xml)->xml_kids.length \ : 0) extern JSXML * js_NewXML(JSContext *cx, JSXMLClass xml_class); extern void js_MarkXML(JSContext *cx, JSXML *xml); extern void js_FinalizeXML(JSContext *cx, JSXML *xml); extern JSObject * js_ParseNodeToXMLObject(JSContext *cx, JSParseNode *pn); extern JSObject * js_NewXMLObject(JSContext *cx, JSXMLClass xml_class); extern JSObject * js_GetXMLObject(JSContext *cx, JSXML *xml); extern JS_FRIEND_DATA(JSXMLObjectOps) js_XMLObjectOps; extern JS_FRIEND_DATA(JSClass) js_XMLClass; extern JS_FRIEND_DATA(JSExtendedClass) js_NamespaceClass; extern JS_FRIEND_DATA(JSExtendedClass) js_QNameClass; extern JS_FRIEND_DATA(JSClass) js_AttributeNameClass; extern JS_FRIEND_DATA(JSClass) js_AnyNameClass; /* * Macros to test whether an object or a value is of type "xml" (per typeof). * NB: jsapi.h must be included before any call to VALUE_IS_XML. */ #define OBJECT_IS_XML(cx,obj) ((obj)->map->ops == &js_XMLObjectOps.base) #define VALUE_IS_XML(cx,v) (!JSVAL_IS_PRIMITIVE(v) && \ OBJECT_IS_XML(cx, JSVAL_TO_OBJECT(v))) extern JSObject * js_InitNamespaceClass(JSContext *cx, JSObject *obj); extern JSObject * js_InitQNameClass(JSContext *cx, JSObject *obj); extern JSObject * js_InitAttributeNameClass(JSContext *cx, JSObject *obj); extern JSObject * js_InitAnyNameClass(JSContext *cx, JSObject *obj); extern JSObject * js_InitXMLClass(JSContext *cx, JSObject *obj); extern JSObject * js_InitXMLClasses(JSContext *cx, JSObject *obj); extern JSBool js_GetFunctionNamespace(JSContext *cx, jsval *vp); extern JSBool js_GetDefaultXMLNamespace(JSContext *cx, jsval *vp); extern JSBool js_SetDefaultXMLNamespace(JSContext *cx, jsval v); /* * Return true if v is a XML QName object, or if it converts to a string that * contains a valid XML qualified name (one containing no :), false otherwise. * NB: This function is an infallible predicate, it hides exceptions. */ extern JSBool js_IsXMLName(JSContext *cx, jsval v); extern JSBool js_ToAttributeName(JSContext *cx, jsval *vp); extern JSString * js_EscapeAttributeValue(JSContext *cx, JSString *str); extern JSString * js_AddAttributePart(JSContext *cx, JSBool isName, JSString *str, JSString *str2); extern JSString * js_EscapeElementValue(JSContext *cx, JSString *str); extern JSString * js_ValueToXMLString(JSContext *cx, jsval v); extern JSBool js_GetAnyName(JSContext *cx, jsval *vp); extern JSBool js_FindXMLProperty(JSContext *cx, jsval name, JSObject **objp, jsval *namep); extern JSBool js_GetXMLProperty(JSContext *cx, JSObject *obj, jsval name, jsval *vp); extern JSBool js_GetXMLFunction(JSContext *cx, JSObject *obj, jsid id, jsval *vp); extern JSBool js_SetXMLProperty(JSContext *cx, JSObject *obj, jsval name, jsval *vp); extern JSBool js_GetXMLDescendants(JSContext *cx, JSObject *obj, jsval id, jsval *vp); extern JSBool js_DeleteXMLListElements(JSContext *cx, JSObject *listobj); extern JSBool js_FilterXMLList(JSContext *cx, JSObject *obj, jsbytecode *pc, jsval *vp); extern JSObject * js_ValueToXMLObject(JSContext *cx, jsval v); extern JSObject * js_ValueToXMLListObject(JSContext *cx, jsval v); extern JSObject * js_CloneXMLObject(JSContext *cx, JSObject *obj); extern JSObject * js_NewXMLSpecialObject(JSContext *cx, JSXMLClass xml_class, JSString *name, JSString *value); extern JSString * js_MakeXMLCDATAString(JSContext *cx, JSString *str); extern JSString * js_MakeXMLCommentString(JSContext *cx, JSString *str); extern JSString * js_MakeXMLPIString(JSContext *cx, JSString *name, JSString *str); #endif /* jsxml_h___ */ pacparser-1.4.5/src/spidermonkey/js/src/lock_SunOS.s000066400000000000000000000077661464010763600224360ustar00rootroot00000000000000! ! The contents of this file are subject to the Netscape 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/NPL/ ! ! Software distributed under the License is distributed on an "AS ! IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or ! implied. See the License for the specific language governing ! rights and limitations under the License. ! ! The Original Code is Mozilla Communicator client code, released ! March 31, 1998. ! ! The Initial Developer of the Original Code is Netscape ! Communications Corporation. Portions created by Netscape are ! Copyright (C) 1998-1999 Netscape Communications Corporation. All ! Rights Reserved. ! ! Contributor(s): ! ! Alternatively, the contents of this file may be used under the ! terms of the GNU Public License (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 NPL, indicate your decision by ! deleting the provisions above and replace 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 NPL or the GPL. ! ! ! atomic compare-and-swap routines for V8 sparc ! and for V8+ (ultrasparc) ! ! ! standard asm linkage macros; this module must be compiled ! with the -P option (use C preprocessor) #include ! ====================================================================== ! ! Perform the sequence *a = b atomically with respect to previous value ! of a (a0). If *a==a0 then assign *a to b, all in one atomic operation. ! Returns 1 if assignment happened, and 0 otherwise. ! ! usage : old_val = compare_and_swap(address, oldval, newval) ! ! ----------------------- ! Note on REGISTER USAGE: ! as this is a LEAF procedure, a new stack frame is not created; ! we use the caller stack frame so what would normally be %i (input) ! registers are actually %o (output registers). Also, we must not ! overwrite the contents of %l (local) registers as they are not ! assumed to be volatile during calls. ! ! So, the registers used are: ! %o0 [input] - the address of the value to increment ! %o1 [input] - the old value to compare with ! %o2 [input] - the new value to set for [%o0] ! %o3 [local] - work register ! ----------------------- #ifndef ULTRA_SPARC ! v8 ENTRY(compare_and_swap) ! standard assembler/ELF prologue stbar mov -1,%o3 ! busy flag swap [%o0],%o3 ! get current value l1: cmp %o3,-1 ! busy? be,a l1 ! if so, spin swap [%o0],%o3 ! using branch-delay to swap back value cmp %o1,%o3 ! compare old with current be,a l2 ! if equal then swap in new value swap [%o0],%o2 ! done. swap [%o0],%o3 ! otherwise, swap back current value retl mov 0,%o0 ! return false l2: retl mov 1,%o0 ! return true SET_SIZE(compare_and_swap) ! standard assembler/ELF epilogue ! ! end ! #else /* ULTRA_SPARC */ ! ====================================================================== ! ! v9 ENTRY(compare_and_swap) ! standard assembler/ELF prologue stbar cas [%o0],%o1,%o2 ! compare *w with old value and set to new if equal cmp %o1,%o2 ! did we succeed? be,a m1 ! yes mov 1,%o0 ! return true (annulled when no jump) mov 0,%o0 ! return false m1: retl nop SET_SIZE(compare_and_swap) ! standard assembler/ELF epilogue ! ! end ! ! ====================================================================== ! #endif pacparser-1.4.5/src/spidermonkey/js/src/perfect.js000066400000000000000000000022131464010763600221770ustar00rootroot00000000000000// Some simple testing of new, eval and some string stuff. // constructor -- expression array initialization function ExprArray(n,v) { // Initializes n values to v coerced to a string. for (var i = 0; i < n; i++) { this[i] = "" + v; } } // Print the perfect numbers up to n and the sum expression for n's divisors. function perfect(n) { print("The perfect numbers up to " + n + " are:"); // We build sumOfDivisors[i] to hold a string expression for // the sum of the divisors of i, excluding i itself. var sumOfDivisors = new ExprArray(n+1,1); for (var divisor = 2; divisor <= n; divisor++) { for (var j = divisor + divisor; j <= n; j += divisor) { sumOfDivisors[j] += " + " + divisor; } // At this point everything up to 'divisor' has its sumOfDivisors // expression calculated, so we can determine whether it's perfect // already by evaluating. if (eval(sumOfDivisors[divisor]) == divisor) { print("" + divisor + " = " + sumOfDivisors[divisor]); } } print("That's all."); } print("\nA number is 'perfect' if it is equal to the sum of its") print("divisors (excluding itself).\n"); perfect(500); pacparser-1.4.5/src/spidermonkey/js/src/plify_jsdhash.sed000066400000000000000000000017561464010763600235500ustar00rootroot00000000000000/ * Double hashing implementation./a\ * GENERATED BY js/src/plify_jsdhash.sed -- DO NOT EDIT!!! / * Double hashing, a la Knuth 6./a\ * GENERATED BY js/src/plify_jsdhash.sed -- DO NOT EDIT!!! s/jsdhash_h___/pldhash_h___/ s/jsdhash\.bigdump/pldhash.bigdump/ s/jstypes\.h/nscore.h/ s/jsbit\.h/prbit.h/ s/jsdhash\.h/pldhash.h/ s/jsdhash\.c/pldhash.c/ s/jsdhash:/pldhash:/ s/jsutil\.h/nsDebug.h/ s/JS_DHASH/PL_DHASH/g s/JS_DHash/PL_DHash/g s/JSDHash/PLDHash/g s/JSHash/PLHash/g s/uint32 /PRUint32/g s/\([^U]\)int32 /\1PRInt32/g s/uint16 /PRUint16/g s/\([^U]\)int16 /\1PRInt16/g s/uint32/PRUint32/g s/\([^U]\)int32/\1PRInt32/g s/uint16/PRUint16/g s/\([^U]\)int16/\1PRInt16/g s/JSBool/PRBool/g s/extern JS_PUBLIC_API(\([^()]*\))/NS_COM_GLUE \1/ s/JS_PUBLIC_API(\([^()]*\))/\1/ s/JS_DLL_CALLBACK/PR_CALLBACK/ s/JS_STATIC_DLL_CALLBACK/PR_STATIC_CALLBACK/ s/JS_NewDHashTable/PL_NewDHashTable/ s/JS_ASSERT(0)/NS_NOTREACHED("0")/ s/\( *\)JS_ASSERT(\(.*\));/\1NS_ASSERTION(\2,\n\1 "\2");/ s/JS_/PR_/g pacparser-1.4.5/src/spidermonkey/js/src/prmjtime.c000066400000000000000000000307471464010763600222210ustar00rootroot00000000000000/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- * * ***** BEGIN LICENSE BLOCK ***** * Version: MPL 1.1/GPL 2.0/LGPL 2.1 * * The contents of this file are subject to the Mozilla Public License Version * 1.1 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * http://www.mozilla.org/MPL/ * * Software distributed under the License is distributed on an "AS IS" basis, * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License * for the specific language governing rights and limitations under the * License. * * The Original Code is Mozilla Communicator client code, released * March 31, 1998. * * The Initial Developer of the Original Code is * Netscape Communications Corporation. * Portions created by the Initial Developer are Copyright (C) 1998 * the Initial Developer. All Rights Reserved. * * Contributor(s): * * Alternatively, the contents of this file may be used under the terms of * either of the GNU General Public License Version 2 or later (the "GPL"), * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), * in which case the provisions of the GPL or the LGPL are applicable instead * of those above. If you wish to allow use of your version of this file only * under the terms of either the GPL or the LGPL, and not to allow others to * use your version of this file under the terms of the MPL, indicate your * decision by deleting the provisions above and replace them with the notice * and other provisions required by the GPL or the LGPL. If you do not delete * the provisions above, a recipient may use your version of this file under * the terms of any one of the MPL, the GPL or the LGPL. * * ***** END LICENSE BLOCK ***** */ /* * PR time code. */ #include "jsstddef.h" #ifdef SOLARIS #define _REENTRANT 1 #endif #include #include #include "jstypes.h" #include "jsutil.h" #include "jsprf.h" #include "prmjtime.h" #define PRMJ_DO_MILLISECONDS 1 #ifdef XP_OS2 #include #endif #ifdef XP_WIN #include #include #endif #if defined(XP_UNIX) || defined(XP_BEOS) #ifdef _SVID_GETTOD /* Defined only on Solaris, see Solaris */ extern int gettimeofday(struct timeval *tv); #endif #include #endif /* XP_UNIX */ #define IS_LEAP(year) \ (year != 0 && ((((year & 0x3) == 0) && \ ((year - ((year/100) * 100)) != 0)) || \ (year - ((year/400) * 400)) == 0)) #define PRMJ_HOUR_SECONDS 3600L #define PRMJ_DAY_SECONDS (24L * PRMJ_HOUR_SECONDS) #define PRMJ_YEAR_SECONDS (PRMJ_DAY_SECONDS * 365L) #define PRMJ_MAX_UNIX_TIMET 2145859200L /*time_t value equiv. to 12/31/2037 */ /* function prototypes */ static void PRMJ_basetime(JSInt64 tsecs, PRMJTime *prtm); /* * get the difference in seconds between this time zone and UTC (GMT) */ JSInt32 PRMJ_LocalGMTDifference() { #if defined(XP_UNIX) || defined(XP_WIN) || defined(XP_OS2) || defined(XP_BEOS) struct tm ltime; /* get the difference between this time zone and GMT */ memset((char *)<ime,0,sizeof(ltime)); ltime.tm_mday = 2; ltime.tm_year = 70; #ifdef SUNOS4 ltime.tm_zone = 0; ltime.tm_gmtoff = 0; return timelocal(<ime) - (24 * 3600); #else return mktime(<ime) - (24L * 3600L); #endif #endif } /* Constants for GMT offset from 1970 */ #define G1970GMTMICROHI 0x00dcdcad /* micro secs to 1970 hi */ #define G1970GMTMICROLOW 0x8b3fa000 /* micro secs to 1970 low */ #define G2037GMTMICROHI 0x00e45fab /* micro secs to 2037 high */ #define G2037GMTMICROLOW 0x7a238000 /* micro secs to 2037 low */ /* Convert from base time to extended time */ static JSInt64 PRMJ_ToExtendedTime(JSInt32 base_time) { JSInt64 exttime; JSInt64 g1970GMTMicroSeconds; JSInt64 low; JSInt32 diff; JSInt64 tmp; JSInt64 tmp1; diff = PRMJ_LocalGMTDifference(); JSLL_UI2L(tmp, PRMJ_USEC_PER_SEC); JSLL_I2L(tmp1,diff); JSLL_MUL(tmp,tmp,tmp1); JSLL_UI2L(g1970GMTMicroSeconds,G1970GMTMICROHI); JSLL_UI2L(low,G1970GMTMICROLOW); #ifndef JS_HAVE_LONG_LONG JSLL_SHL(g1970GMTMicroSeconds,g1970GMTMicroSeconds,16); JSLL_SHL(g1970GMTMicroSeconds,g1970GMTMicroSeconds,16); #else JSLL_SHL(g1970GMTMicroSeconds,g1970GMTMicroSeconds,32); #endif JSLL_ADD(g1970GMTMicroSeconds,g1970GMTMicroSeconds,low); JSLL_I2L(exttime,base_time); JSLL_ADD(exttime,exttime,g1970GMTMicroSeconds); JSLL_SUB(exttime,exttime,tmp); return exttime; } JSInt64 PRMJ_Now(void) { #ifdef XP_OS2 JSInt64 s, us, ms2us, s2us; struct timeb b; #endif #ifdef XP_WIN JSInt64 s, us, win2un = JSLL_INIT(0x19DB1DE, 0xD53E8000), ten = JSLL_INIT(0, 10); FILETIME time, midnight; #endif #if defined(XP_UNIX) || defined(XP_BEOS) struct timeval tv; JSInt64 s, us, s2us; #endif /* XP_UNIX */ #ifdef XP_OS2 ftime(&b); JSLL_UI2L(ms2us, PRMJ_USEC_PER_MSEC); JSLL_UI2L(s2us, PRMJ_USEC_PER_SEC); JSLL_UI2L(s, b.time); JSLL_UI2L(us, b.millitm); JSLL_MUL(us, us, ms2us); JSLL_MUL(s, s, s2us); JSLL_ADD(s, s, us); return s; #endif #ifdef XP_WIN /* The windows epoch is around 1600. The unix epoch is around 1970. win2un is the difference (in windows time units which are 10 times more precise than the JS time unit) */ GetSystemTimeAsFileTime(&time); /* Win9x gets confused at midnight http://support.microsoft.com/default.aspx?scid=KB;en-us;q224423 So if the low part (precision <8mins) is 0 then we get the time again. */ if (!time.dwLowDateTime) { GetSystemTimeAsFileTime(&midnight); time.dwHighDateTime = midnight.dwHighDateTime; } JSLL_UI2L(s, time.dwHighDateTime); JSLL_UI2L(us, time.dwLowDateTime); JSLL_SHL(s, s, 32); JSLL_ADD(s, s, us); JSLL_SUB(s, s, win2un); JSLL_DIV(s, s, ten); return s; #endif #if defined(XP_UNIX) || defined(XP_BEOS) #ifdef _SVID_GETTOD /* Defined only on Solaris, see Solaris */ gettimeofday(&tv); #else gettimeofday(&tv, 0); #endif /* _SVID_GETTOD */ JSLL_UI2L(s2us, PRMJ_USEC_PER_SEC); JSLL_UI2L(s, tv.tv_sec); JSLL_UI2L(us, tv.tv_usec); JSLL_MUL(s, s, s2us); JSLL_ADD(s, s, us); return s; #endif /* XP_UNIX */ } /* Get the DST timezone offset for the time passed in */ JSInt64 PRMJ_DSTOffset(JSInt64 local_time) { JSInt64 us2s; time_t local; JSInt32 diff; JSInt64 maxtimet; struct tm tm; PRMJTime prtm; #ifndef HAVE_LOCALTIME_R struct tm *ptm; #endif JSLL_UI2L(us2s, PRMJ_USEC_PER_SEC); JSLL_DIV(local_time, local_time, us2s); /* get the maximum of time_t value */ JSLL_UI2L(maxtimet,PRMJ_MAX_UNIX_TIMET); if(JSLL_CMP(local_time,>,maxtimet)){ JSLL_UI2L(local_time,PRMJ_MAX_UNIX_TIMET); } else if(!JSLL_GE_ZERO(local_time)){ /*go ahead a day to make localtime work (does not work with 0) */ JSLL_UI2L(local_time,PRMJ_DAY_SECONDS); } JSLL_L2UI(local,local_time); PRMJ_basetime(local_time,&prtm); #ifndef HAVE_LOCALTIME_R ptm = localtime(&local); if(!ptm){ return JSLL_ZERO; } tm = *ptm; #else localtime_r(&local,&tm); /* get dst information */ #endif diff = ((tm.tm_hour - prtm.tm_hour) * PRMJ_HOUR_SECONDS) + ((tm.tm_min - prtm.tm_min) * 60); if(diff < 0){ diff += PRMJ_DAY_SECONDS; } JSLL_UI2L(local_time,diff); JSLL_MUL(local_time,local_time,us2s); return(local_time); } /* Format a time value into a buffer. Same semantics as strftime() */ size_t PRMJ_FormatTime(char *buf, int buflen, char *fmt, PRMJTime *prtm) { #if defined(XP_UNIX) || defined(XP_WIN) || defined(XP_OS2) || defined(XP_BEOS) struct tm a; /* Zero out the tm struct. Linux, SunOS 4 struct tm has extra members int * tm_gmtoff, char *tm_zone; when tm_zone is garbage, strftime gets * confused and dumps core. NSPR20 prtime.c attempts to fill these in by * calling mktime on the partially filled struct, but this doesn't seem to * work as well; the result string has "can't get timezone" for ECMA-valid * years. Might still make sense to use this, but find the range of years * for which valid tz information exists, and map (per ECMA hint) from the * given year into that range. * N.B. This hasn't been tested with anything that actually _uses_ * tm_gmtoff; zero might be the wrong thing to set it to if you really need * to format a time. This fix is for jsdate.c, which only uses * JS_FormatTime to get a string representing the time zone. */ memset(&a, 0, sizeof(struct tm)); a.tm_sec = prtm->tm_sec; a.tm_min = prtm->tm_min; a.tm_hour = prtm->tm_hour; a.tm_mday = prtm->tm_mday; a.tm_mon = prtm->tm_mon; a.tm_wday = prtm->tm_wday; a.tm_year = prtm->tm_year - 1900; a.tm_yday = prtm->tm_yday; a.tm_isdst = prtm->tm_isdst; /* Even with the above, SunOS 4 seems to detonate if tm_zone and tm_gmtoff * are null. This doesn't quite work, though - the timezone is off by * tzoff + dst. (And mktime seems to return -1 for the exact dst * changeover time.) */ #if defined(SUNOS4) if (mktime(&a) == -1) { /* Seems to fail whenever the requested date is outside of the 32-bit * UNIX epoch. We could proceed at this point (setting a.tm_zone to * "") but then strftime returns a string with a 2-digit field of * garbage for the year. So we return 0 and hope jsdate.c * will fall back on toString. */ return 0; } #endif return strftime(buf, buflen, fmt, &a); #endif } /* table for number of days in a month */ static int mtab[] = { /* jan, feb,mar,apr,may,jun */ 31,28,31,30,31,30, /* july,aug,sep,oct,nov,dec */ 31,31,30,31,30,31 }; /* * basic time calculation functionality for localtime and gmtime * setups up prtm argument with correct values based upon input number * of seconds. */ static void PRMJ_basetime(JSInt64 tsecs, PRMJTime *prtm) { /* convert tsecs back to year,month,day,hour,secs */ JSInt32 year = 0; JSInt32 month = 0; JSInt32 yday = 0; JSInt32 mday = 0; JSInt32 wday = 6; /* start on a Sunday */ JSInt32 days = 0; JSInt32 seconds = 0; JSInt32 minutes = 0; JSInt32 hours = 0; JSInt32 isleap = 0; JSInt64 result; JSInt64 result1; JSInt64 result2; JSInt64 base; JSLL_UI2L(result,0); JSLL_UI2L(result1,0); JSLL_UI2L(result2,0); /* get the base time via UTC */ base = PRMJ_ToExtendedTime(0); JSLL_UI2L(result, PRMJ_USEC_PER_SEC); JSLL_DIV(base,base,result); JSLL_ADD(tsecs,tsecs,base); JSLL_UI2L(result, PRMJ_YEAR_SECONDS); JSLL_UI2L(result1,PRMJ_DAY_SECONDS); JSLL_ADD(result2,result,result1); /* get the year */ while ((isleap == 0) ? !JSLL_CMP(tsecs,<,result) : !JSLL_CMP(tsecs,<,result2)) { /* subtract a year from tsecs */ JSLL_SUB(tsecs,tsecs,result); days += 365; /* is it a leap year ? */ if(IS_LEAP(year)){ JSLL_SUB(tsecs,tsecs,result1); days++; } year++; isleap = IS_LEAP(year); } JSLL_UI2L(result1,PRMJ_DAY_SECONDS); JSLL_DIV(result,tsecs,result1); JSLL_L2I(mday,result); /* let's find the month */ while(((month == 1 && isleap) ? (mday >= mtab[month] + 1) : (mday >= mtab[month]))){ yday += mtab[month]; days += mtab[month]; mday -= mtab[month]; /* it's a Feb, check if this is a leap year */ if(month == 1 && isleap != 0){ yday++; days++; mday--; } month++; } /* now adjust tsecs */ JSLL_MUL(result,result,result1); JSLL_SUB(tsecs,tsecs,result); mday++; /* day of month always start with 1 */ days += mday; wday = (days + wday) % 7; yday += mday; /* get the hours */ JSLL_UI2L(result1,PRMJ_HOUR_SECONDS); JSLL_DIV(result,tsecs,result1); JSLL_L2I(hours,result); JSLL_MUL(result,result,result1); JSLL_SUB(tsecs,tsecs,result); /* get minutes */ JSLL_UI2L(result1,60); JSLL_DIV(result,tsecs,result1); JSLL_L2I(minutes,result); JSLL_MUL(result,result,result1); JSLL_SUB(tsecs,tsecs,result); JSLL_L2I(seconds,tsecs); prtm->tm_usec = 0L; prtm->tm_sec = (JSInt8)seconds; prtm->tm_min = (JSInt8)minutes; prtm->tm_hour = (JSInt8)hours; prtm->tm_mday = (JSInt8)mday; prtm->tm_mon = (JSInt8)month; prtm->tm_wday = (JSInt8)wday; prtm->tm_year = (JSInt16)year; prtm->tm_yday = (JSInt16)yday; } pacparser-1.4.5/src/spidermonkey/js/src/prmjtime.h000066400000000000000000000065651464010763600222270ustar00rootroot00000000000000/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- * * ***** BEGIN LICENSE BLOCK ***** * Version: MPL 1.1/GPL 2.0/LGPL 2.1 * * The contents of this file are subject to the Mozilla Public License Version * 1.1 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * http://www.mozilla.org/MPL/ * * Software distributed under the License is distributed on an "AS IS" basis, * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License * for the specific language governing rights and limitations under the * License. * * The Original Code is Mozilla Communicator client code, released * March 31, 1998. * * The Initial Developer of the Original Code is * Netscape Communications Corporation. * Portions created by the Initial Developer are Copyright (C) 1998 * the Initial Developer. All Rights Reserved. * * Contributor(s): * * Alternatively, the contents of this file may be used under the terms of * either of the GNU General Public License Version 2 or later (the "GPL"), * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), * in which case the provisions of the GPL or the LGPL are applicable instead * of those above. If you wish to allow use of your version of this file only * under the terms of either the GPL or the LGPL, and not to allow others to * use your version of this file under the terms of the MPL, indicate your * decision by deleting the provisions above and replace them with the notice * and other provisions required by the GPL or the LGPL. If you do not delete * the provisions above, a recipient may use your version of this file under * the terms of any one of the MPL, the GPL or the LGPL. * * ***** END LICENSE BLOCK ***** */ #ifndef prmjtime_h___ #define prmjtime_h___ /* * PR date stuff for mocha and java. Placed here temporarily not to break * Navigator and localize changes to mocha. */ #include #include "jslong.h" #ifdef MOZILLA_CLIENT #include "jscompat.h" #endif JS_BEGIN_EXTERN_C typedef struct PRMJTime PRMJTime; /* * Broken down form of 64 bit time value. */ struct PRMJTime { JSInt32 tm_usec; /* microseconds of second (0-999999) */ JSInt8 tm_sec; /* seconds of minute (0-59) */ JSInt8 tm_min; /* minutes of hour (0-59) */ JSInt8 tm_hour; /* hour of day (0-23) */ JSInt8 tm_mday; /* day of month (1-31) */ JSInt8 tm_mon; /* month of year (0-11) */ JSInt8 tm_wday; /* 0=sunday, 1=monday, ... */ JSInt16 tm_year; /* absolute year, AD */ JSInt16 tm_yday; /* day of year (0 to 365) */ JSInt8 tm_isdst; /* non-zero if DST in effect */ }; /* Some handy constants */ #define PRMJ_USEC_PER_SEC 1000000L #define PRMJ_USEC_PER_MSEC 1000L /* Return the current local time in micro-seconds */ extern JSInt64 PRMJ_Now(void); /* get the difference between this time zone and gmt timezone in seconds */ extern JSInt32 PRMJ_LocalGMTDifference(void); /* Format a time value into a buffer. Same semantics as strftime() */ extern size_t PRMJ_FormatTime(char *buf, int buflen, char *fmt, PRMJTime *tm); /* Get the DST offset for the local time passed in */ extern JSInt64 PRMJ_DSTOffset(JSInt64 local_time); JS_END_EXTERN_C #endif /* prmjtime_h___ */ pacparser-1.4.5/src/spidermonkey/js/src/resource.h000066400000000000000000000006131464010763600222130ustar00rootroot00000000000000//{{NO_DEPENDENCIES}} // Microsoft Developer Studio generated include file. // Used by js3240.rc // // Next default values for new objects // #ifdef APSTUDIO_INVOKED #ifndef APSTUDIO_READONLY_SYMBOLS #define _APS_NEXT_RESOURCE_VALUE 101 #define _APS_NEXT_COMMAND_VALUE 40001 #define _APS_NEXT_CONTROL_VALUE 1000 #define _APS_NEXT_SYMED_VALUE 101 #endif #endif pacparser-1.4.5/src/spidermonkey/js/src/rules.mk000066400000000000000000000121001464010763600216700ustar00rootroot00000000000000# -*- Mode: makefile -*- # # ***** BEGIN LICENSE BLOCK ***** # Version: MPL 1.1/GPL 2.0/LGPL 2.1 # # The contents of this file are subject to the Mozilla Public License Version # 1.1 (the "License"); you may not use this file except in compliance with # the License. You may obtain a copy of the License at # http://www.mozilla.org/MPL/ # # Software distributed under the License is distributed on an "AS IS" basis, # WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License # for the specific language governing rights and limitations under the # License. # # The Original Code is Mozilla Communicator client code, released # March 31, 1998. # # The Initial Developer of the Original Code is # Netscape Communications Corporation. # Portions created by the Initial Developer are Copyright (C) 1998-1999 # the Initial Developer. All Rights Reserved. # # Contributor(s): # Michael Ang # # Alternatively, the contents of this file may be used under the terms of # either of the GNU General Public License Version 2 or later (the "GPL"), # or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), # in which case the provisions of the GPL or the LGPL are applicable instead # of those above. If you wish to allow use of your version of this file only # under the terms of either the GPL or the LGPL, and not to allow others to # use your version of this file under the terms of the MPL, indicate your # decision by deleting the provisions above and replace them with the notice # and other provisions required by the GPL or the LGPL. If you do not delete # the provisions above, a recipient may use your version of this file under # the terms of any one of the MPL, the GPL or the LGPL. # # ***** END LICENSE BLOCK ***** # # JSRef GNUmake makefile rules # ifdef USE_MSVC LIB_OBJS = $(addprefix $(OBJDIR)/, $(LIB_CFILES:.c=.obj)) PROG_OBJS = $(addprefix $(OBJDIR)/, $(PROG_CFILES:.c=.obj)) else LIB_OBJS = $(addprefix $(OBJDIR)/, $(LIB_CFILES:.c=.o)) LIB_OBJS += $(addprefix $(OBJDIR)/, $(LIB_ASFILES:.s=.o)) PROG_OBJS = $(addprefix $(OBJDIR)/, $(PROG_CFILES:.c=.o)) endif CFILES = $(LIB_CFILES) $(PROG_CFILES) OBJS = $(LIB_OBJS) $(PROG_OBJS) ifdef USE_MSVC # TARGETS = $(LIBRARY) # $(PROGRAM) not supported for MSVC yet TARGETS += $(SHARED_LIBRARY) $(PROGRAM) # it is now else TARGETS += $(LIBRARY) $(SHARED_LIBRARY) $(PROGRAM) endif all: +$(LOOP_OVER_PREDIRS) ifneq "$(strip $(TARGETS))" "" $(MAKE) -f Makefile.ref $(TARGETS) endif +$(LOOP_OVER_DIRS) $(OBJDIR)/%: %.c @$(MAKE_OBJDIR) $(CC) -o $@ $(CFLAGS) $*.c $(LDFLAGS) # This rule must come before the rule with no dep on header $(OBJDIR)/%.o: %.c %.h @$(MAKE_OBJDIR) $(CC) -o $@ -c $(CFLAGS) $*.c $(OBJDIR)/%.o: %.c @$(MAKE_OBJDIR) $(CC) -o $@ -c $(CFLAGS) $*.c $(OBJDIR)/%.o: %.s @$(MAKE_OBJDIR) $(AS) -o $@ $(ASFLAGS) $*.s # This rule must come before rule with no dep on header $(OBJDIR)/%.obj: %.c %.h @$(MAKE_OBJDIR) $(CC) -Fo$(OBJDIR)/ -c $(CFLAGS) $(JSDLL_CFLAGS) $*.c $(OBJDIR)/%.obj: %.c @$(MAKE_OBJDIR) $(CC) -Fo$(OBJDIR)/ -c $(CFLAGS) $(JSDLL_CFLAGS) $*.c $(OBJDIR)/js.obj: js.c @$(MAKE_OBJDIR) $(CC) -Fo$(OBJDIR)/ -c $(CFLAGS) $< ifeq ($(OS_ARCH),OS2) $(LIBRARY): $(LIB_OBJS) $(AR) $@ $? $(AR_OS2_SUFFIX) $(RANLIB) $@ else ifdef USE_MSVC $(SHARED_LIBRARY): $(LIB_OBJS) link.exe $(LIB_LINK_FLAGS) /base:0x61000000 $(OTHER_LIBS) \ /out:"$@" /pdb:none\ /implib:"$(OBJDIR)/$(@F:.dll=.lib)" $^ else $(LIBRARY): $(LIB_OBJS) $(AR) rv $@ $? $(RANLIB) $@ $(SHARED_LIBRARY): $(LIB_OBJS) $(MKSHLIB) -o $@ $(LIB_OBJS) $(LDFLAGS) $(OTHER_LIBS) endif endif # Java stuff $(CLASSDIR)/$(OBJDIR)/$(JARPATH)/%.class: %.java mkdir -p $(@D) $(JAVAC) $(JAVAC_FLAGS) $< define MAKE_OBJDIR if test ! -d $(@D); then rm -rf $(@D); mkdir -p $(@D); fi endef ifdef DIRS LOOP_OVER_DIRS = \ @for d in $(DIRS); do \ if test -d $$d; then \ set -e; \ echo "cd $$d; $(MAKE) -f Makefile.ref $@"; \ cd $$d; $(MAKE) -f Makefile.ref $@; cd ..; \ set +e; \ else \ echo "Skipping non-directory $$d..."; \ fi; \ done endif ifdef PREDIRS LOOP_OVER_PREDIRS = \ @for d in $(PREDIRS); do \ if test -d $$d; then \ set -e; \ echo "cd $$d; $(MAKE) -f Makefile.ref $@"; \ cd $$d; $(MAKE) -f Makefile.ref $@; cd ..; \ set +e; \ else \ echo "Skipping non-directory $$d..."; \ fi; \ done endif export: +$(LOOP_OVER_PREDIRS) mkdir -p $(DIST)/include $(DIST)/$(LIBDIR) $(DIST)/bin ifneq "$(strip $(HFILES))" "" $(CP) $(HFILES) $(DIST)/include endif ifneq "$(strip $(LIBRARY))" "" $(CP) $(LIBRARY) $(DIST)/$(LIBDIR) endif ifneq "$(strip $(JARS))" "" $(CP) $(JARS) $(DIST)/$(LIBDIR) endif ifneq "$(strip $(SHARED_LIBRARY))" "" $(CP) $(SHARED_LIBRARY) $(DIST)/$(LIBDIR) endif ifneq "$(strip $(PROGRAM))" "" $(CP) $(PROGRAM) $(DIST)/bin endif +$(LOOP_OVER_DIRS) clean: rm -rf $(OBJS) $(GARBAGE) @cd fdlibm; $(MAKE) -f Makefile.ref clean clobber: rm -rf $(OBJS) $(TARGETS) $(DEPENDENCIES) @cd fdlibm; $(MAKE) -f Makefile.ref clobber depend: gcc -MM $(CFLAGS) $(LIB_CFILES) tar: tar cvf $(TARNAME) $(TARFILES) gzip $(TARNAME) pacparser-1.4.5/src/spidermonkey/js/src/win32.order000066400000000000000000000223251464010763600222160ustar00rootroot00000000000000js_MarkGCThing ; 5893956 JS_GetPrivate ; 2090130 JS_HashTableRawLookup ; 1709984 js_Mark ; 1547496 js_GetToken ; 1406677 js_UngetToken ; 1154416 js_MarkAtom ; 992874 js_MatchToken ; 980277 js_CompareStrings ; 662772 js_Lock ; 628184 js_Unlock ; 628184 js_AtomizeString ; 611102 js_HashString ; 611102 js_DropScopeProperty ; 546476 JS_malloc ; 484350 js_Atomize ; 464433 js_InflateStringToBuffer ; 460739 js_HoldScopeProperty ; 442612 JS_free ; 382991 js_MarkScript ; 376942 js_HashId ; 365238 JS_CompareValues ; 352366 js_IdToValue ; 337594 JS_GetClass ; 325296 js_LookupProperty ; 324680 js_GetAtom ; 244669 js_DropProperty ; 223217 JS_GetParent ; 209680 js_LiveContext ; 205767 js_PeekToken ; 200646 js_GetSlotThreadSafe ; 198839 JS_GetStringChars ; 190862 JS_HashTableRawAdd ; 179156 js_FoldConstants ; 162626 js_EmitTree ; 145634 JS_EnumerateStub ; 140640 js_NewSrcNote ; 136983 js_GetProperty ; 135639 js_NewScopeProperty ; 135057 js_MutateScope ; 135057 js_GetMutableScope ; 135057 js_AllocSlot ; 132401 JS_GetRuntime ; 127316 JS_FrameIterator ; 121963 JS_GetFrameFunctionObject ; 120567 js_AllocGCThing ; 119828 js_DestroyScopeProperty ; 115989 js_Emit3 ; 109135 js_AtomizeChars ; 108038 JS_HashTableLookup ; 107154 JS_InstanceOf ; 103905 js_DefineProperty ; 99514 js_strncpy ; 88276 js_PeekTokenSameLine ; 87197 js_HoldObjectMap ; 79084 js_DropObjectMap ; 77824 js_NewObject ; 72421 js_ValueToString ; 72143 js_GetClassPrototype ; 66235 js_UnlockRuntime ; 64699 js_LockRuntime ; 64699 js_ContextIterator ; 64586 JS_ClearWatchPointsForObject ; 64155 js_FinalizeObject ; 63925 js_IndexAtom ; 63789 JS_SetPrivate ; 63702 JS_GetGlobalObject ; 63546 js_Emit1 ; 63012 JS_ContextIterator ; 57847 JS_GetInstancePrivate ; 57817 JS_HashTableRawRemove ; 57057 js_AllocRawStack ; 54181 js_Invoke ; 53568 js_FindProperty ; 53150 JS_GetFrameScript ; 51395 js_LinkFunctionObject ; 50651 js_SetSrcNoteOffset ; 47735 js_InWithStatement ; 47346 js_NewFunction ; 47074 js_NewSrcNote2 ; 46165 JS_HashTableAdd ; 45503 JS_HashTableRemove ; 45213 js_InCatchBlock ; 42198 js_AddRootRT ; 40587 js_AddRoot ; 40587 js_SetProperty ; 40558 JS_AddNamedRoot ; 40462 js_RemoveRoot ; 40384 JS_RemoveRootRT ; 38129 js_NewString ; 37471 js_DefineFunction ; 36629 JS_GetContextThread ; 36498 JS_LookupProperty ; 35137 JS_ValueToString ; 34072 JS_realloc ; 33776 JS_DefineFunction ; 33268 JS_SetErrorReporter ; 32851 js_FinalizeString ; 30311 js_FinalizeStringRT ; 30311 JS_ArenaAllocate ; 30099 JS_BeginRequest ; 29323 JS_EndRequest ; 29323 JS_GetContextPrivate ; 29189 JS_CompactArenaPool ; 28874 js_ValueToStringAtom ; 27934 JS_ValueToId ; 26517 js_ValueToBoolean ; 25908 JS_InternString ; 25467 js_PopStatement ; 24364 js_PushStatement ; 24364 js_NewStringCopyN ; 23911 js_FlushPropertyCacheByProp ; 23883 js_GetStringBytes ; 23421 JS_ArenaRelease ; 23267 JS_GetStringBytes ; 23106 js_FreeStack ; 22399 js_AllocStack ; 22399 JS_SetProperty ; 21240 js_InitObjectMap ; 19991 js_NewScope ; 19991 js_strlen ; 19070 JS_GetScriptPrincipals ; 18063 js_SrcNoteLength ; 17369 js_DestroyObjectMap ; 17198 js_DestroyScope ; 17198 JS_GetStringLength ; 16306 js_PopStatementCG ; 15418 JS_GetFrameAnnotation ; 14949 js_FreeRawStack ; 14032 js_Interpret ; 14032 js_TransferScopeLock ; 13899 JS_ResolveStandardClass ; 13645 JS_ResumeRequest ; 12837 JS_SuspendRequest ; 12837 JS_GetProperty ; 12488 JS_NewObject ; 11660 js_AllocTryNotes ; 11418 js_NewNumberValue ; 10859 js_InternalInvoke ; 10051 js_NewDouble ; 9936 js_SetJumpOffset ; 9886 js_SkipWhiteSpace ; 9299 js_NewDoubleValue ; 7474 JS_GetPendingException ; 7404 js_NewObjectMap ; 7236 JS_ClearPendingException ; 7092 JS_strtod ; 7053 js_strtod ; 7053 js_InflateString ; 7004 JS_GetFunctionName ; 6808 JS_NewHashTable ; 6794 JS_NewFunction ; 6575 js_FreeSlot ; 6476 js_LockScope ; 6332 JS_HashTableEnumerateEntries ; 6285 js_GetLengthProperty ; 6162 js_LockObj ; 6149 JS_NewUCStringCopyN ; 5994 JS_NewNumberValue ; 5904 js_NewStringCopyZ ; 5809 JS_NewUCStringCopyZ ; 5809 js_DeflateString ; 5612 js_ValueToNumber ; 5456 JS_SetOptions ; 5322 js_NewScript ; 4941 js_InitCodeGenerator ; 4810 js_FinishTakingSrcNotes ; 4810 js_NewScriptFromParams ; 4810 js_InitAtomMap ; 4810 js_FinishTakingTryNotes ; 4810 js_NewScriptFromCG ; 4810 js_FinishCodeGenerator ; 4810 JS_strdup ; 4534 JS_HashTableDestroy ; 4119 js_CheckRedeclaration ; 3965 JS_DefineFunctions ; 3808 js_EmitFunctionBody ; 3739 js_TryMethod ; 3685 js_DefaultValue ; 3610 js_CloneFunctionObject ; 3577 JS_InitClass ; 3546 js_SetClassPrototype ; 3377 JS_GetPrototype ; 3268 JS_DefineProperties ; 3115 js_FindVariable ; 3093 js_DestroyScript ; 3041 JS_ClearScriptTraps ; 3041 js_FreeAtomMap ; 3041 JS_NewStringCopyZ ; 2953 js_AtomizeObject ; 2709 JS_ValueToBoolean ; 2643 js_SetLengthProperty ; 2637 JS_GetOptions ; 2593 js_ValueToObject ; 2522 js_ValueToNonNullObject ; 2510 js_StringToObject ; 2482 JS_SetElement ; 2448 js_NumberToString ; 2407 JS_TypeOfValue ; 2275 js_NewBufferTokenStream ; 2253 js_NewTokenStream ; 2253 js_CloseTokenStream ; 2253 JS_RemoveRoot ; 2148 JS_NewDouble ; 2129 JS_vsnprintf ; 1937 JS_snprintf ; 1937 JS_CallFunctionValue ; 1844 JS_DHashVoidPtrKeyStub ; 1840 JS_DHashTableOperate ; 1840 js_SetProtoOrParent ; 1758 js_DoubleToInteger ; 1729 JS_SetVersion ; 1531 js_ValueToFunction ; 1476 JS_SetPrototype ; 1408 JS_CeilingLog2 ; 1317 js_Execute ; 1199 js_CompileFunctionBody ; 1182 JS_CompileUCFunctionForPrincipals ; 1182 js_GetSrcNoteOffset ; 1139 JS_DHashMatchEntryStub ; 1094 JS_VersionToString ; 1090 JS_CompileUCScriptForPrincipals ; 1071 js_CompileTokenStream ; 1071 js_CurrentThreadId ; 1058 JS_IdToValue ; 1046 js_ConstructObject ; 974 JS_DestroyScript ; 967 js_PCToLineNumber ; 967 JS_DefineProperty ; 930 JS_GetScriptFilename ; 924 JS_GetFramePC ; 899 JS_EvaluateUCScriptForPrincipals ; 892 JS_PCToLineNumber ; 848 JS_StringToVersion ; 761 js_ExecuteRegExp ; 755 JS_MaybeGC ; 717 JS_ValueToNumber ; 698 JS_GetVersion ; 698 JS_AliasProperty ; 693 js_AtomizeValue ; 664 js_BooleanToString ; 664 js_SetSlotThreadSafe ; 596 JS_DHashClearEntryStub ; 584 JS_DHashTableRawRemove ; 584 JS_DefineObject ; 557 js_PutCallObject ; 516 js_GetCallObject ; 516 js_strchr ; 511 JS_DefineUCProperty ; 480 JS_dtostr ; 475 JS_ValueToInt32 ; 464 js_ValueToInt32 ; 464 JS_FinishArenaPool ; 453 js_NewTryNote ; 441 js_strtointeger ; 437 JS_vsmprintf ; 428 JS_DHashTableInit ; 423 JS_DHashAllocTable ; 423 JS_DHashGetStubOps ; 423 JS_NewDHashTable ; 423 JS_DHashTableDestroy ; 423 JS_DHashFreeTable ; 423 JS_DHashTableFinish ; 423 js_EmitBreak ; 412 js_GetAttributes ; 412 JS_DefineConstDoubles ; 407 JS_ArenaGrow ; 374 js_AtomizeInt ; 372 JS_SetParent ; 345 JS_CloneFunctionObject ; 343 JS_IsNativeFrame ; 343 JS_ReportErrorNumber ; 340 js_ErrorToException ; 340 js_ReportErrorNumberVA ; 340 js_GetErrorMessage ; 340 js_ExpandErrorArguments ; 340 js_ReportUncaughtException ; 315 JS_IsExceptionPending ; 315 js_ReportErrorAgain ; 315 js_ErrorFromException ; 315 JS_LookupUCProperty ; 307 JS_InitArenaPool ; 293 PRMJ_Now ; 262 DllMain@12 ; 235 JS_ExecuteScript ; 232 JS_GetFrameFunction ; 226 PRMJ_LocalGMTDifference ; 175 JS_GetConstructor ; 175 JS_SetGlobalObject ; 164 js_LockGCThing ; 155 js_NewRegExpObject ; 152 js_NewRegExp ; 152 js_InitObjectClass ; 131 js_InitFunctionClass ; 131 js_EmitN ; 128 JS_ArenaFinish ; 124 js_GC ; 124 js_SweepAtomState ; 124 js_MarkAtomState ; 124 JS_ArenaRealloc ; 124 js_ForceGC ; 124 js_FlushPropertyCache ; 122 js_InitNumberClass ; 114 JS_smprintf ; 112 js_DoubleToECMAInt32 ; 112 js_ValueToECMAInt32 ; 111 JS_ValueToECMAInt32 ; 111 JS_SetContextPrivate ; 109 PRMJ_DSTOffset ; 108 js_Clear ; 105 JS_ClearScope ; 105 JS_NewScriptObject ; 104 JS_smprintf_free ; 104 JS_ConvertValue ; 99 js_GetSrcNote ; 98 JS_ValueToECMAUint32 ; 93 js_ValueToECMAUint32 ; 93 js_printf ; 93 js_DoubleToECMAUint32 ; 93 js_DestroyRegExp ; 89 js_UnlockGCThing ; 89 js_TryValueOf ; 87 js_NewSrcNote3 ; 86 JS_ConvertStub ; 81 JS_SetPendingException ; 80 js_InitStringClass ; 79 JS_GC ; 78 js_InitArrayClass ; 74 js_InitDateClass ; 67 JS_NewContext ; 64 JS_AddArgumentFormatter ; 64 js_InitContextForLocking ; 64 js_NewContext ; 64 JS_SetBranchCallback ; 64 JS_ClearRegExpStatics ; 64 js_InitRegExpStatics ; 64 js_InitCallClass ; 63 js_InitRegExpClass ; 61 js_Enumerate ; 58 JS_DestroyContext ; 46 js_DestroyContext ; 46 js_FreeRegExpStatics ; 46 js_InitScanner ; 39 js_NewPrinter ; 36 js_DestroyPrinter ; 36 js_GetPrinterOutput ; 36 JS_FreeArenaPool ; 36 js_DecompileCode ; 34 js_EmitContinue ; 33 js_CheckAccess ; 30 js_DecompileValueGenerator ; 28 js_InitMathClass ; 27 js_InitExceptionClasses ; 25 js_NewArrayObject ; 24 js_InitArgumentsClass ; 21 js_puts ; 20 js_InitBooleanClass ; 19 JS_InitStandardClasses ; 19 js_InitScriptClass ; 19 js_obj_toString ; 15 js_GetArgsValue ; 14 js_GetArgsObject ; 14 js_AtomizeDouble ; 12 JS_DestroyIdArray ; 11 js_NewIdArray ; 11 JS_GetElement ; 11 JS_EvaluateScript ; 9 JS_EvaluateUCScript ; 9 JS_DecompileFunction ; 8 js_DecompileFunction ; 8 JS_NewString ; 8 js_SetStringBytes ; 8 JS_GetArrayLength ; 7 JS_NewArrayObject ; 7 JS_IsArrayObject ; 7 JS_ValueToObject ; 7 JS_DefineElement ; 6 js_DecompileScript ; 6 JS_PushArguments ; 4 JS_PopArguments ; 4 JS_PushArgumentsVA ; 4 js_PutArgsObject ; 2 JS_SetGCCallbackRT ; 2 JS_Init ; 1 js_SetupLocks ; 1 js_InitRuntimeNumberState ; 1 js_InitRuntimeStringState ; 1 js_InitLock ; 1 js_InitGC ; 1 js_InitAtomState ; 1 js_InitStringGlobals ; 1 pacparser-1.4.5/tests/000077500000000000000000000000001464010763600146525ustar00rootroot00000000000000pacparser-1.4.5/tests/proxy.pac000066400000000000000000000022451464010763600165230ustar00rootroot00000000000000// Go direct for plain hostnames and any host in .manugarg.com domain except // for www and www.manugarg.com. // Go via proxy for all other hosts. function FindProxyForURL(url, host) { if ( (isPlainHostName(host) || dnsDomainIs(host, '.manugarg.com')) && !localHostOrDomainIs(host, 'www.manugarg.com') ) return 'plainhost/.manugarg.com'; // Test single quote handling in URL. if (/'/.test(url)) { return 'URLHasQuotes'; } // Return externaldomain if host matches .*\.externaldomain\.com if (/.*\.externaldomain\.com/.test(host)) return 'externaldomain'; // Test if DNS resolving is working as intended if (dnsDomainIs(host, '.google.com') && isResolvable(host)) return 'isResolvable'; // Test if DNS resolving is working as intended if (dnsDomainIs(host, '.notresolvabledomainXXX.com') && !isResolvable(host)) return 'isNotResolvable'; if (/^https:\/\/.*$/.test(url)) return 'secureUrl'; if (isInNet(myIpAddress(), '10.10.0.0', '255.255.0.0')) return '10.10.0.0'; if ( typeof myIpAddressEx == 'function' && isInNetEx(myIpAddressEx(), '3ffe:8311:ffff/48') ) return '3ffe:8311:ffff'; else return 'END-OF-SCRIPT'; } pacparser-1.4.5/tests/runtests.py000066400000000000000000000060111464010763600171110ustar00rootroot00000000000000# Copyright (C) 2007 Manu Garg. # Author: Manu Garg # # pacparser is a library that provides methods to parse proxy auto-config # (PAC) files. Please read README file included with this package for more # information about this library. # # pacparser is free software; you can redistribute it and/or # modify it under the terms of the GNU Lesser General Public # License as published by the Free Software Foundation; either # version 2.1 of the License, or (at your option) any later version. # pacparser is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU # Lesser General Public License for more details. # You should have received a copy of the GNU Lesser General Public # License along with this library; if not, write to the Free Software # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, # USA. import getopt import glob import os import sys def module_path(tests_dir): py_ver = '*'.join([str(x) for x in sys.version_info[0:2]]) builddir = os.path.join(tests_dir, '..', 'src', 'pymod', 'build') print('Build dir: %s', builddir) print(os.listdir(builddir)) return glob.glob(os.path.join(builddir, 'lib*%s' % py_ver))[0] def runtests(pacfile, testdata, tests_dir): try: pacparser_module_path = module_path(tests_dir) except Exception: raise Exception('Tests failed. Could not determine pacparser path.') if 'DEBUG' in os.environ: print('Pacparser module path: %s' % pacparser_module_path) sys.path.insert(0, pacparser_module_path) try: import pacparser except ImportError: raise Exception('Tests failed. Could not import pacparser.') if 'DEBUG' in os.environ: print('Imported pacparser module: %s' % sys.modules['pacparser']) f = open(testdata) for line in f: comment = '' if '#' in line: comment = line.split('#', 1)[1] line = line.split('#', 1)[0].strip() if not line: continue if ('NO_INTERNET' in os.environ and os.environ['NO_INTERNET'] and 'INTERNET_REQUIRED' in comment): continue if 'DEBUG' in os.environ: print(line) (params, expected_result) = line.strip().split('|') args = dict(getopt.getopt(params.split(), 'eu:c:')[0]) if '-e' in args: pacparser.enable_microsoft_extensions() if '-c' in args: pacparser.setmyip(args['-c']) pacparser.init() pacparser.parse_pac_file(pacfile) result = pacparser.find_proxy(args['-u']) pacparser.cleanup() if result != expected_result: raise Exception('Tests failed. Got "%s", expected "%s"' % (result, expected_result)) print('All tests were successful.') def main(): tests_dir = os.path.dirname(os.path.join(os.getcwd(), sys.argv[0])) pacfile = os.path.join(tests_dir, 'proxy.pac') testdata = os.path.join(tests_dir, 'testdata') runtests(pacfile, testdata, tests_dir) if __name__ == '__main__': main() pacparser-1.4.5/tests/runtests.sh000077500000000000000000000034101464010763600170760ustar00rootroot00000000000000#!/usr/bin/env bash pushd $(dirname $0) > /dev/null; script_dir=$PWD; popd > /dev/null pactester=$script_dir/../src/pactester pacfile=$script_dir/proxy.pac testdata=$script_dir/testdata library_path=$script_dir/../src export DYLD_LIBRARY_PATH=$library_path:$DYLD_LIBRARY_PATH export LD_LIBRARY_PATH=$library_path:$LD_LIBRARY_PATH lib=$library_path/libpacparser.so.1 os_arch=$(uname -s | sed /\ /s//_/) if [ "$os_arch" = "Darwin" ]; then lib=$library_path/libpacparser.1.dylib fi if test ! -f "$lib"; then echo "Test failed. pacparser library not found." exit 1 fi while read line do comment="${line#*#}" line="${line%%#*}" line=${line%"${line##*[^[:space:]]}"} test -z "$line" && continue # If machine is not connected to the internet and a test requires internet # just skip that test. test ! -z $NO_INTERNET && \ test "${comment/INTERNET_REQUIRED/}" != "${comment}" && \ continue params=${line%%|*} expected_result=${line##*|} result=$($pactester -p $pacfile $params) if [ $? != 0 ]; then echo "pactester execution failed." echo "Command tried: $pactester -p $pacfile $params" echo "Running with debug mode on..." echo "DEBUG=1 $pactester -p $pacfile $params" DEBUG=1 $pactester -p $pacfile $params exit 1 fi [ $DEBUG ] && echo "Test line: $line" [ $DEBUG ] && echo "Params: $params" if [ "$result" != "$expected_result" ]; then echo "Test failed: got \"$result\", expected \"$expected_result\"" echo "Command tried: $pactester -p $pacfile $params" echo "Running with debug mode on..." echo "DEBUG=1 $pactester -p $pacfile $params" DEBUG=1 $pactester -p $pacfile $params exit 1; fi done < $testdata echo "All tests were successful." pacparser-1.4.5/tests/testdata000066400000000000000000000010711464010763600164050ustar00rootroot00000000000000# Command Line Parameters|Expected Result -c 3ffe:8311:ffff:1:0:0:0:0 -u http://www.somehost.com|3ffe:8311:ffff -c 0.0.0.0 -u http://www.google.co.in|END-OF-SCRIPT -u http://host1|plainhost/.manugarg.com -u http://www1.manugarg.com|plainhost/.manugarg.com -u http://www.manugarg.org/test'o'rama|URLHasQuotes -u http://manugarg.externaldomain.com|externaldomain -u http://www.google.com|isResolvable # INTERNET_REQUIRED -u http://www.notresolvabledomainXXX.com|isNotResolvable -u https://www.somehost.com|secureUrl -c 10.10.100.112 -u http://www.somehost.com|10.10.0.0 pacparser-1.4.5/tools/000077500000000000000000000000001464010763600146505ustar00rootroot00000000000000pacparser-1.4.5/tools/buildmacpackage.sh000077500000000000000000000020011464010763600202740ustar00rootroot00000000000000#!/bin/sh -e # # This script creates a Mac package, that can be directly installed by # Installer on Mac. ver=$1 [ -z $ver ] && echo "Please specify package version." && exit # $stage_dir is where we'll install our package files. stage_dir=/tmp/pacparser_$RANDOM sudo rm -rf /tmp/pacparser* mkdir -p $stage_dir if [ ! -e src/Makefile ]; then echo "Call this script from the root of the source directory" exit 1 fi # Build pactester and pacparser library and install in $stage_dir make -C src DESTDIR=$stage_dir make -C src install # Build python module and install it in $stage_dir make -C src pymod DESTDIR=$stage_dir make -C src install-pymod sudo chown -R root:wheel ${stage_dir} pkgbuild --root ${stage_dir} --identifier pacparser --version ${ver} pacparser.pkg sudo rm -rf $stage_dir # Build disk image tmp_dir=/tmp/pacparser-$ver-$RANDOM disk_image=pacparser-$ver.dmg rm -rf $tmp_dir $disk_image mkdir $tmp_dir mv pacparser.pkg $tmp_dir hdiutil create -srcfolder $tmp_dir pacparser-$ver.dmg rm -rf $tmp_dir pacparser-1.4.5/tools/generatedocs.sh000077500000000000000000000017641464010763600176620ustar00rootroot00000000000000#!/bin/bash tools_dir=$(dirname $0) if [ "${tools_dir:0:1}" != "/" ]; then tools_dir=$PWD/$tools_dir fi docs_dir=$tools_dir/../docs src_dir=$tools_dir/../src tmpdir=$TMPDIR/pacparser_doxygen_temp_$$ mkdir -p $tmpdir cd $tmpdir cp $src_dir/pacparser.h . doxygen $docs_dir/doxygen.config if [ $? != 0 ]; then echo "Doxygen returned error. Not continuing." exit fi mkdir -p $docs_dir/html # Fix HTMLs. mv html/group__pacparser.html $docs_dir/html/pacparser.html mv html/doxygen.css $docs_dir/html/ sed -i '' -e 's/group__pacparser.html//g' $docs_dir/html/pacparser.html # Remove Doxygen logo. sed -i '' -e '/doxygen\.png/s/^.*$/Doxygen/g' $docs_dir/html/pacparser.html mkdir -p $docs_dir/man/man3 mv man/man3/* $docs_dir/man/man3/ # Remove unnecessary and bad file deprecated.3 rm -f $docs_dir/man/man3/deprecated.3 # Fix man page. sed -i '' -e 's/pacparser \\\-/pacparser - Library to parse proxy auto-confg (PAC) files./g' $docs_dir/man/man3/*.3 cd - echo $tmpdir # Cleanup temp dir rm -rf $tmpdir pacparser-1.4.5/tools/package000077500000000000000000000033501464010763600161720ustar00rootroot00000000000000#!/bin/bash -e USAGE=" Usage: $0 [-n] [-v version]\n -n : Create a new release. Tag the current repository revision as version\n -v version: Specify the version. If not specified, the latest hg tag is used. \n This option is required for a new release.\n " while getopts "nv:" flag do if [ "$flag" = "n" ]; then newrelease="yes" elif [ "$flag" = "v" ]; then ver=$OPTARG elif [ "$flag" = "?" ]; then echo -e $USAGE exit 2 fi done if [ "$newrelease" = "yes" ]; then if [ -z "$ver" ];then echo "Version should be specified while creating a new release." echo -e $USAGE exit 2 else echo -n "Are you sure you want to create a new release and tag the current " echo -n "repository revision as $ver (y/n): " read response if [ $response != "y" ]; then echo "You said no. Not moving ahead." exit 0 fi git tag $ver fi fi if [ -z "$ver" ]; then ver=$(git describe --always --tags --candidate=100 |\ awk -F- 'NR == 1 {print $1 "-" $2}') fi cd $(dirname $0); tools_dir=$PWD; cd - > /dev/null pkgdir=$tools_dir/packages/pacparser-$ver; rm -rf $pkgdir*; mkdir -p $pkgdir pkg=pacparser-${ver}.tar.gz mkdir -p $tools_dir/packages pushd $tools_dir/.. > /dev/null git archive --format=tar HEAD | (cd $pkgdir && tar xf - --exclude debian) # Create a version string. This is used at the time of build. echo "VERSION=$ver" > $pkgdir/src/version.mk # Create the tarball. pushd $tools_dir/packages > /dev/null tar czf $(basename $pkg) $(basename $pkgdir) rm -rf $pkgdir; mkdir -p $pkgdir popd > /dev/null # Create the directory to be used for debian package. git archive --format=tar HEAD | (cd $pkgdir && tar xf -) echo "VERSION=$ver" > $pkgdir/src/version.mk popd > /dev/null